1.深/浅拷贝
编译器为我们提供的合成拷贝构造函数以及合成的拷贝赋值运算符都是浅拷贝。浅拷贝只是做简单的复制,如果在类的构造函数中new出了内存,浅拷贝只会简单的复制一份指向该内存的指针,而不会再开辟内存,这就会使得程序运行出现内存错误,如此,当对象析构的时候,会delete多次同一块内存区域,发生错误。这也就是为什么,必要的时候需要我们自己编写拷贝构造函数和重载赋值运算符,让它变成深拷贝,深拷贝即在copy指针的时候不是简单做值copy,而是还要开辟内存。
2.构造函数析构函数调用顺序练习题
如果下面的调用过程不用编译器都能快速知道,那么拷贝构造函数和析构函数的调用过程就基本掌握了。
1 #include2 #include 3 using namespace std; 4 class Copy_construction { 5 public: 6 Copy_construction(int a,int b,int c) 7 { 8 this->a = a; 9 this->b = b;10 this->c = c;11 cout << "这是Copy_constructiond的有默认参数的构造函数! "< a<<" "< b<<" "< c< a << " " << this->b << " " << this->c << endl;17 }18 19 int getC()20 {21 return a;22 }23 private:24 int a;25 int b;26 int c;27 28 };29 30 class Test {31 public:32 Test():obj1(1,2,3),obj2(4,5,6),m(100)33 {34 cout << "Test() 构造函数\n";35 }36 ~Test()37 {38 cout << "Test 对象析构了\n";39 }40 Test(const Test &obj):obj1(7,8,9),obj2(10,11,12),m(1)41 {42 cout << "Test(const Test &obj) 拷贝构造函数调用了 \n";43 }44 45 Copy_construction obj1;46 Copy_construction obj2;47 int m;48 };49 void play_empty(Test mytest)50 {51 cout << "play_empty(Test mytest) " << mytest.obj1.getC()<
运行结果:
summary:
最好使用构造函数的初始化列表,而不是用赋值,虽然c++11支持类内初始值,但是目前大多项目都是不使用类内初始值而用构造函数初始化列表代替,可能是为了维护之前的代码吧。初始化列表要注意的一点是,成员初始化顺序最好与它们在类内声明中出现的顺序一致。比如:
calss X{
int i;
int j;
public:
X(int val) :j(val),i(j) {}//编译器先初始化i,因为i在j之前出现在类中,这会让一个垃圾值j去初始化i。所以要特别注意这一点。
};
比较友好的编译器可能会对构造函数初始值列表中的数据成员顺序与这些成员的声明顺序不一致时发出警告。Qt creator和gcc 5.4.0会发出警告,vs2015则不会^_^。
如果类的属性有const修饰的标识符,如const int a;此时可以采用类内初始值,但是为了兼容c++11之前的代码,我们采用const int a;然后的a的值在类的构造函数初始值列表上完成初始化,不能在构造函数内部给const的标识符赋值,只能在初始值列表处初始化。