首先,我是Java编码器,想要了解c ++中的多态性.我为了学习目的编写了这个例子:
#include<iostream>
using namespace std;
class A
{
public:
virtual void foo(){ std::cout << "foo" << std::endl; }
};
class B : public A
{
public:
void foo(){ std::cout << "overriden foo" << std::endl; }
};
A c = B();
int main(){ c.foo(); } //prints foo, not overriden foo
Run Code Online (Sandbox Code Playgroud)
我预计overriden foo会打印,但事实并非如此.为什么?我们覆盖了该方法foo,class B并且我认为决定应该调用哪个方法是从对象的运行时类型开始,在我的例子中B,但不是静态类型(A在我的例子中).
现场的例子就在那里
orl*_*rlp 22
当你这样做:
A c = B();
Run Code Online (Sandbox Code Playgroud)
你转换的B价值为A.你不希望这样.
您应该创建一个B对象并通过A 指针或引用访问它以获取多态行为:
B b;
A& c = b;
Run Code Online (Sandbox Code Playgroud)
小智 14
在java中,你有类似和的类型的值语义,并且你有其他所有的引用语义.intfloat
情况并非如此C++:类型系统是统一的,您可以获得您要求的任何值或引用语义.
用你编写的代码
A c = B()
Run Code Online (Sandbox Code Playgroud)
你告诉编译器创建类型的新值B,然后转换它类型的值A,存储的价值在c.在这种情况下,转换意味着A从B您创建它的新实例中获取数据,并将其复制到A存储在其中的新实例中c.
你可以这样做:
B b;
A &c = b;
Run Code Online (Sandbox Code Playgroud)
这仍然创造了价值 b,但现在c是一个参考来A,这意味着c现在将参照B创建,而不是它的一个副本实例A的一部分.
现在,这仍然b作为局部变量创建,并且存储的对象b一旦b超出范围就会被销毁.如果你想要更持久的东西,你需要使用指针 ; 例如
shared_ptr<A> c = make_shared<B>();
c->foo();
Run Code Online (Sandbox Code Playgroud)
你可以做更像'原始'的事情
A *b = new B();
Run Code Online (Sandbox Code Playgroud)
但这是一个"愚蠢"的指针; shared_ptr更聪明,当没有别的东西引用它时你的物体就会被摧毁.如果你做了后者,你必须在适当的时候自己做破坏(并且弄乱它是一个常见的错误来源)
您的困惑源于Java和C++之间的重要区别.
在Java中,如果你写
MyClass var = whatever;
Run Code Online (Sandbox Code Playgroud)
您的变量var是对返回的对象的引用whatever.但是,在C++中,此语法意味着" 通过将表达式的结果传递给适当的构造函数来创建类型MyClass的新对象whatever,并将结果对象复制到变量中var.
特别是,您的代码创建了一个A名为的新类型对象,并将类型c的临时默认构造对象传递B给其复制构造函数(因为这是唯一适合的构造函数).由于新创建的对象是类型A而不是类型B,因此显然调用了A方法foo.
如果要引用对象,则必须通过添加&类型在C++中明确请求.但是,对非常量对象的引用不能绑定到临时对象.因此,您还需要显式声明绑定的对象(或者,使用对const对象的引用,并修复您的foo成员函数const,因为它们无论如何都不会更改对象).因此,最简单的代码版本可以执行您想要的内容:
// your original definitions of A and B assumed here
B b; // The object of type B
A& c = b; // c is now a *reference* to b
int main() { c.foo(); } // calls B::foo() thanks to polymorphism
Run Code Online (Sandbox Code Playgroud)
然而,更好的版本将是const-correct,然后可以使用您的原始构造:
#include <iostream>
class A
{
public:
virtual void foo() const // note the additional const here!
{ std::cout << "foo" << std::endl; }
};
class B : public A
{
public:
void foo() const // and also const here
{ std::cout << "overridden foo" << std::endl; }
};
A const& c = B(); // Since we bind to a const reference,
// the lifetime of the temporary is extended to the
// lifetime of the reference
int main() { c.foo(); } //prints overridden foo
Run Code Online (Sandbox Code Playgroud)
(注意我删除了,using namespace std;因为这是一件坏事(你的代码std::无论如何都是显式的,所以它只是多余的).
但请注意,C++引用仍然与Java引用不同,因为它们无法重新分配; 任何赋值都转到底层对象.例如:
#include <iostream>
class A { public: virtual void foo() const { std::cout << "I'm an A\n"; } };
class B: public A { public: void foo() const { std::cout << "I'm a B\n"; } };
class C: public A { public: void foo() const { std::cout << "I'm a C\n"; } };
B b;
C c;
int main()
{
A& ref = b; // bind reference ref to object b
ref.foo(); // outputs "I'm a B"
ref = c; // does *not* re-bind the reference to c, but calls A::operator= (which in this case is a no-op)
ref.foo(); // again outputs "I'm a B"
}
Run Code Online (Sandbox Code Playgroud)
如果要更改引用的对象,则必须使用指针:
// definitions of A, B and C as above
int main()
{
A* prt = &b; // pointer ptr points to b
prt->foo(); // outputs "I'm a B"
prt = &c; // reassign ptr to point to c
prt->foo(); // outputs "I'm a C"
}
Run Code Online (Sandbox Code Playgroud)