c++复制构造函数-小但重要的区别
C++ copy constructor - small but important difference
我不明白这里发生了什么,觉得很奇怪,在了解了原因之后,我认为分享答案对别人的时间是有价值的。
给出这个简单的代码:
#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;
}
嗯,因为我刚刚开始了解虚拟机制,所以我认为两个测试会产生相同的结果(打印圈)。虽然test2中是这样,但在test1中不是这样。
为了理解其中的原因,我写下了在后台真实发生的事情。
Test1:1. 程序执行"Circle()"行。1.1调用Shape的默认构造函数(因为Circle是从Shape派生的)。1.2调用Circle的默认构造函数
- 程序执行动作"Shape Shape ="。这实际上调用了Shape的复制构造函数。*在这里,你应该注意到复制构造函数不会复制_vptr,因为_vptr是Circle中的不可见字段。它只复制a的值并返回(*this)。这就是为什么它不打印圆圈的真正原因。
我还有一个问题。当运行test1时,我得到了这样的输出:默认形状构造函数默认的Circle构造函数形状复制构造函数圆distructor形状distructor打印形状,数字是8形状distructor
如果复制构造函数签名是Shape(const Shape&s),根据这个输出,在实际创建形状为形状之前,有一个复制构造函数调用。怎么会这样?
Test2:1. 类Circle的新实例正在堆上构建。(执行new Circle行)2. 在堆内存中返回一个指向该地址的指针,并以指针的形式放置。该地址的前四个字节是指向Circle虚表的指针。这就是为什么test1不同于test2。
重要的是要理解测试之间的区别与test1在堆栈上构建一个Circle而test2在堆上构建一个Circle无关。嗯,实际上它和它有关系。但真正的原因是复制构造函数没有复制_vptr。
通过复制(非多态)到基类型来将类"切片"
参见c++中的思考
Shape shape = Circle();
这里没有赋值,因此没有调用赋值操作符。这里使用的=
是初始化。Circle()
创建一个临时Circle
对象,然后将该临时对象的Shape
部分复制构造到shape
中。这个临时对象将被销毁,因为它不再需要了。
Shape* shape = new Circle();
一个Circle
对象是动态创建的(在堆上),并返回指向该对象的指针。shape
指针指向Circle
对象的Shape
部分。"vptr未被复制"并不是造成差异的原因,而是一种结果。你写了两个测试,做两种完全不同的事情,所以你得到了完全不同的结果。"不同的vptr"仅仅是一个不同的结果。
在c++编程时,你几乎不需要担心底层实现细节,比如"vptr"和相关的东西。应该可以在语言级别上对代码进行推理,并且只在调查性能和调试最糟糕的问题时关注实现细节。
我不知道为什么你认为operator=
在shape
被构造之前被调用——实际上operator=
从来没有被调用过。
Shape shape
的虚拟成员表现得好像shape
是Shape
而不是Circle
的真正原因是shape
实际上不是Circle
,而且从来都不是。c++标准要求这样做。Shape shape
没有Circle
的任何成员,Circle
的数据成员也没有分配空间,在数据不存在的情况下尝试使用虚函数是相当疯狂的。
Shape shape
创建一个Shape
的实例,无论它是如何初始化的
由于其他人已经指出了您的代码的问题,至于为什么您不会对两个测试函数获得相同的结果,我还有一些其他的东西要说,您可以尝试一下,以更好地理解虚拟机制是如何工作的。
既然您已经使用指针来实现运行时多态性,现在让我们尝试使用引用。看到我的修改是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()
。
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- C++ - "!pointer"和"pointer == nullptr"的区别?
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 在 const 函数中通过引用和指针返回之间的区别
- 我想知道长双倍和双倍之间的区别
- 返回递归调用和仅递归调用的区别
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- (double) 和 double() 之间的区别
- & 和 * 之间的区别
- std::is_convertible 和 std::convertible_to 之间的区别(在实践中)?
- 析构函数和'delete'之间的区别
- 从预序遍历构造 bst 的 c++ 和 python 解决方案之间的区别
- vector.back() 和 vector[vector.size() - 1] 之间的区别?
- 函数签名与调用的函数不匹配,常量字符[]和字符*之间的区别?
- OpenMP #pragma omp for v/s #pragma omp parallel for 之间的区别?
- boost::function 和 std::tr1::function 之间是否有重要的区别需要了解
- 在IDE中运行程序和通过.exe运行程序之间有重要的区别吗?
- c++复制构造函数-小但重要的区别