我无法弄清楚这里发生了什么,认为这很奇怪,在了解了原因后我认为分享答案对某个人的时间是有价值的。
所以给出了这个简单的代码:
#include <iostream>
using namespace std;
class Shape {
public:
int* a;
Shape(){
cout<<"Default Shape constructor"<<endl;
a = new int(8); // default
}
Shape(int n){
a = new int(n);
cout<<"Shape(n) constructor"<<endl;
}
// copy constructor
Shape(const Shape& s){
cout<<"Shape copy constructor"<<endl;
a = new int(*(s.a));
}
Shape& operator=(const Shape& s){
cout<<"Shape operator="<<endl;
if (&s == (this))
return (*this);
// this.clear();
a = new int(*(s.a));
return (*this);
}
virtual void draw(){
cout<<"Print Shape the number is "<<*a<<endl;
};
virtual ~Shape(){
delete a;
cout<<"Shape distructor"<<endl;
}
};
class Circle : public Shape {
public:
int b;
Circle() {
cout<<"Default Circle constructor"<<endl;
b=0;
}
virtual void draw() {
cout<<"Printing Circle. The number is "<<b<<endl;
}
~Circle(){
cout<<"Circle distructor"<<endl;
}
};
为什么以下两个测试给出了两个不同的答案:
static void test1(){
Shape shape = Circle() ;
shape.draw();
}
static void test2(){
Shape* shape = new Circle() ;
shape->draw();
delete shape;
}
好吧,因为我刚刚开始了解虚拟机制,我认为两个测试都会产生相同的结果(打印Circle)。虽然这是 test2 中发生的情况,但在 test1中并非如此。
为了理解原因,我写了背景中真正发生的事情。
测试1: 1.程序执行“ Circle()”行。 1.1调用Shape的默认构造函数(因为Circle是从Shape派生的)。 1.2调用Circle的默认构造函数。
我在这里有另一个问题。 运行test1时我得到了这个输出: 默认形状构造函数 默认Circle构造函数 形状复制构造函数 圆形破坏者 形状破坏者 打印形状数量为8 形状析构
如果复制构造函数签名是 Shape(const Shape&amp; s),根据此输出,在实际创建 shape 之前,会调用复制构造函数作为形状。 这怎么可能发生?
的Test2: 1.正在堆上构建一个新的Circle类实例。 (执行 new Circle 行) 2.返回指向堆内存中该地址的指针,并将其放置在指针形状中。在该地址的前四个字节中,指向Circle的虚拟表的指针。这就是test1与test2不同的原因。
重要的是要理解测试之间的区别与test1在堆栈上构建Circle并且test2在堆上构建Circle这一事实无关。嗯,实际上它与它有关。但真正的原因是复制构造函数不复制_vptr。
答案 0 :(得分:7)
通过将(非多态)复制到基类型来称为“切片”类
有关后台处理的信息,请参阅Thinking in C++
答案 1 :(得分:6)
Shape shape = Circle();
此处没有赋值,因此没有调用赋值运算符。此处使用的=
是初始化。 Circle
创建一个临时Circle()
对象,然后将该临时对象的Shape
部分复制到shape
中。然后销毁临时对象,因为不再需要它。
Shape* shape = new Circle();
动态创建Circle
对象(在堆上),并返回指向该对象的指针。 shape
指针指向此Shape
对象的Circle
部分。 “vptr没有被复制”不是造成差异的原因,这是一种影响。你已经编写了两个测试,它们做了两件完全不同的事情,所以你得到了完全不同的结果。 “不同的vptr”只是一个不同的结果。
在使用C ++进行编程时,您几乎不需要担心像“vptr”这样的低级实现细节以及相关的事情。应该可以在语言级别推断代码,并且在调查性能和调试最棘手的问题时只关注实现细节。
答案 2 :(得分:0)
我不知道为什么你认为在构造operator=
之前调用shape
- 事实上operator=
永远不会被调用。
C ++标准中的任何地方都没有 vptr 。虚拟成员呼叫Shape shape
的行为就像shape
Shape
而不是Circle
一样,shape
真的不是{{1}从来没有。 C ++标准要求它是这样的。 Circle
没有Shape shape
的任何成员,没有为Circle
的数据成员分配空间,尝试在数据时使用虚函数会相当疯狂不存在。
Circle
都会创建Shape shape
的实例。
答案 3 :(得分:0)
正如其他人已经指出你的代码存在问题一样,为什么你不会为这两个测试函数得到相同的结果,我还有别的东西可以说你可以试验一下,以便更好地理解虚拟机制的工作原理。
由于您已经使用指针来实现运行时多态性,现在让我们尝试使用 references 。请参阅我的修改test1()
:
static void test1(){
Circle circle;
Shape & shape = circle; //note &
shape.draw();
}
static void test2(){
Shape* shape = new Circle() ;
shape->draw();
delete shape;
}
现在这两个功能都会打印相同的东西。
底线是:在C ++中,运行时多态性只能通过指针和引用实现,其静态类型是基类,动态类型是它指向的对象/是指。
让我们做更多实验:
Circle circle;
circle.draw();
Shape & s1 = circle;
s1.draw();
Shape & s2 = s1;
s2.draw();
Shape & s3 = s2;
s3.draw();
s2.draw()
和s3.draw()
会做什么?答案是:他们会像s1.draw()
和circle.draw()
那样做。手段,所有这些都会调用Circle::draw()
,没有人会调用Shape::draw()
。