[英]Polymorphism in C++: Calling an overridden method
First, I'm Java coder and want to understand polymorphism in c++. 首先,我是Java编码器,想要了解c ++中的多态性。 I wrote the example for learning purposes:
我为了学习目的编写了这个例子:
#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
I expected that overriden foo
would be printed, but it wasn't. 我希望打印
overriden foo
,但事实并非如此。 Why? 为什么? We overrode the method
foo
in the class B
and I thought that the decision which method should be called is being making from the runtime type of the object which in my case is B
, but not a static type ( A
in my case). 我们覆盖了
class B
的方法foo
,我认为决定应该调用哪个方法是从对象的运行时类型开始,在我的例子中是B
,但不是静态类型(在我的例子中是A
)。
When you do this: 当你这样做:
A c = B();
You're converting the B
value into A
. 你转换
B
值成A
。 You don't want that. 你不希望这样。
You should make a B
object and access it through an A
pointer or reference to get polymorphic behaviour: 您应该创建一个
B
对象并通过A
指针或引用访问它以获得多态行为:
B b;
A& c = b;
In java, you have value semantics with types like int
and float
, and you have reference semantics with everything else. 在java中,您具有类型为
int
和float
值语义 ,并且您具有其他所有内容的引用语义 。
That's not the case in C++
: the type system is unified, and you get whichever of value or reference semantics that you ask for . 在
C++
并非如此:类型系统是统一的,您可以获得您要求的任何值或引用语义。
With the code you've written 用你编写的代码
A c = B()
you've told the compiler to create a new value of type B
, and then convert it to a value of type A
, storing the value in c
. 你告诉编译器创建一个类型
B
的新值,然后将其转换为类型A
的值 ,将值存储在c
。 Conversion, in this case, means taking A
data out of the new B
instance you created it, and copying it into the new A
instance stored in c
. 在这种情况下,转换意味着将
A
数据从您创建它的新B
实例中取出,并将其复制到存储在c
的新A
实例中。
You could do this instead: 你可以这样做:
B b;
A &c = b;
This still creates the value b
, but now c
is a reference to A
, which means c
will now refer to the B
instance you created, rather than being a copy of its A
part. 这仍然会创建值
b
,但现在c
是对A
的引用 ,这意味着c
现在将引用您创建的B
实例,而不是其A
部分的副本。
Now, this still creates b
as a local variable, and the object stored in b
gets destroyed as soon as b
goes out of scope. 现在,这仍然创建
b
作为局部变量,并且一旦b
超出范围,存储在b
的对象就会被销毁。 If you wanted something more persistent, you'd need to use pointers ; 如果你想要更持久的东西,你需要使用指针 ; eg something like
例如
shared_ptr<A> c = make_shared<B>();
c->foo();
You could do something more 'raw' like 你可以做更像'原始'的事情
A *b = new B();
but this is a 'dumb' pointer; 但这是一个“愚蠢”的指针;
shared_ptr
is smarter and your object will get destroyed when nothing else references it. shared_ptr
更聪明,当没有其他东西引用它时,你的对象将被销毁。 If you do the latter, you'd have to do the destruction yourself when appropriate (and messing this up is a common source of mistakes) 如果你做了后者,你必须在适当的时候自己做破坏(并且弄乱它是一个常见的错误来源)
Your confusion stems from a crucial difference between Java and C++. 您的困惑源于Java和C ++之间的重要区别。
In Java if you write 在Java中,如果你写
MyClass var = whatever;
your variable var
is a reference to the object returned by whatever
. 你的变量
var
是对 whatever
返回的对象的引用 。 However, in C++ this syntax means "create a new object of type MyClass
by passing the result of the expression whatever
to an appropriate constructor, and copy the resulting object into the variable var
. 然而,在C ++中此语法表示“创建类型的新对象
MyClass
通过将表达式的结果whatever
一种适当的构造,并且将所得对象复制到变量var
。
In particular, your code creates a new object of type A
, named c
, and passes a temporary default-constructed object of type B
to its copy constructor (because that's the only constructor that fits). 特别是,您的代码创建了
A
名为c
的类型为A
的新对象,并将类型为B
的临时默认构造对象传递给其复制构造函数(因为这是唯一适合的构造函数)。 Since the newly created object is of type A
, not of type B
, obviously A
's method foo
is called. 由于新创建的对象是类型
A
而不是类型B
,因此显然调用了A
的方法foo
。
If you want to have a reference to an object, you have to explicitly request that in C++, by adding &
to the type. 如果要引用对象,则必须在C ++中通过向类型添加
&
来显式请求。 However a reference to non-constant objects cannot be bound to temporaries. 但是,对非常量对象的引用不能绑定到临时对象。 therefore you need to explicitly declare also the object you bind to (or alternatively, use a reference to a const object, and fix your
foo
member functions to be const
, since they don't change the object anyway). 因此,您还需要显式声明绑定的对象(或者,使用对const对象的引用,并将
foo
成员函数修复为const
,因为它们无论如何都不会更改对象)。 So the simplest version of your code doing what you want would read: 因此,最简单的代码版本可以执行您想要的内容:
// 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
However the better version would be const-correct, and then could use your original construction: 然而,更好的版本将是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
(note that I removed using namespace std;
because it's a bad thing to do (and your code used explicit std::
anyway, so it's just redundant). (注意我
using namespace std;
删除了using namespace std;
因为这是一件坏事(你的代码使用显式的std::
反正,所以它只是多余的)。
Note however, that C++ references are still different from Java references in that they cannot be reassigned; 但请注意,C ++引用仍然与Java引用不同,因为它们无法重新分配; any assignment goes to the underlying object instead.
任何赋值都转到底层对象。 For example:
例如:
#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"
}
If you want to change the object you refer to, you'll have to use pointers: 如果要更改引用的对象,则必须使用指针:
// 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"
}
The line of interest is this (using uniform initialization syntax instead): 感兴趣的是这个(使用统一的初始化语法):
A c = B{};
It is important to note that, when declared this way, c
behaves like a value. 重要的是要注意,当以这种方式声明时,
c
行为类似于值。
Your code constructs a local A
named c
out of an instance of B
. 您的代码构造了
B
实例中名为c
的本地A
This is called slicing: Any part of that B
that isn't also part of an A
has been "sliced" away, leaving only an A
. 这称为切片:
B
中不属于A
任何部分已被“切掉”,仅留下A
Giving a symbol reference semantics in C++ (called indirection) requires a different notation. 在C ++中给出符号引用语义(称为间接)需要不同的表示法。
For example: 例如:
A &c = B{};
A d = B{}; // Casts the intermediate B instance to const A &,
// then copy-constructs an A
c.foo(); // calls B::foo because c points to a B through an A interface
d.foo(); // calls A::foo because d is only an instance of an A
Note that the lifetime of the intermediate B
to which c
points is automatically extended to the scope of c
. 注意,
c
点的中间B
的寿命自动扩展到c
的范围。 On the other hand, the second intermediate B
is destroyed after the construction of d
has completed. 另一方面,在完成
d
的构造之后,第二中间体B
被破坏。
In C++, references are immutable (they cannot be changed after initialization). 在C ++中,引用是不可变的(初始化后不能更改它们)。 When used in an expression, it is as though the object (value) to which they are pointing were used instead:
在表达式中使用时,就好像它们所指向的对象(值)一样:
A &c = B{};
c = A{}; // Calls the compiler-provided A::operator = (const A &)
// (a virtual assignment operator with this signature
// was not defined).
// This DOES NOT change where c points.
Pointers, on the other hand, can be changed: 另一方面,指针可以改变:
A a{};
B b{};
A *cptr = &b;
cptr->foo(); // calls B::foo
cptr = &a;
cptr->foo(); // calls A::foo
exactly what orlp said. 究竟是什么orlp说的。 you should also learn to use pointers too (they are fun)
你也应该学会使用指针(它们很有趣)
A* c = new B();
c->foo(); // overriden foo
delete c;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.