参考:C++-copy constructor、copy-assignment operator、destructor
Copy constructors, assignment operators, and exception safe assignment
C++在对象的不同创建方法中,会调用不同的构造函数,下面的代码探讨了调用一般的默认构造函数和复制构造函数的情形
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 private: 7 int v; 8 public: 9 A() 10 { 11 v = 0; 12 cout << "object created" << endl; 13 } 14 A(const A& a) { cout << "copy construct - const" << endl;} 15 A(A& a) { cout << "copy construct" << endl;} 16 ~A() { cout << "object deleted" << endl; } 17 void setvalue(int value = 6); 18 int getvalue() { return v; } 19 }; 20 21 void A::setvalue(int value) 22 { 23 v = value; 24 } 25 26 void ReferenceA(A& a) 27 { 28 cout << "Reference a" << endl; 29 } 30 31 void ParameterA(A a) 32 { 33 cout << "Parameter a" << endl; 34 } 35 36 int main() 37 { 38 A a1; //calls A(), before function return, calls ~A() 39 A *b = new A(); //calls only A() 40 delete b; //calls only ~A(), it must be with new 41 42 A a2 = a1; //calls A(const A& a), before function return, calls ~A() 43 A a3(a1); //calls A(const A& a), before function return, calls ~A() 44 A(a4); //calls A(), before function return, calls ~A() 45 //A a6 = A(a5); //error: a5 no declaration 46 A a6 = A(a4); //calls A(const A& a), before function return, calls ~A() 47 //A *a8 = new A(a7); //error: a7 no declaration 48 A *a8 = new A(a1); //calls only A(const A& a) 49 delete a8; //calls only ~A(), it must be with new 50 ReferenceA(a1); //don't call construction 51 ParameterA(a1); //calls A(const A& a), before function ParameterA return, calls ~A() 52 53 a1.setvalue(); 54 cout << "v = " << a1.getvalue() << endl; 55 //system("pause"); 56 57 return 0; 58 }
在上述代码中,要注意函数的默认参数只能出现在函数的定义或声明中,不能同时出现在定义和声明中。同时要说明的是,如果同时存在A(const A& a)和A(A& a),若出现了调用复制构造函数的情况,将会优先调用A(A& a)。
使用msvc2013编译器所得结果如下
object created object created object deleted copy construct copy construct object created copy construct copy construct object deleted Reference a copy construct Parameter a object deleted v = 6 object deleted object deleted object deleted object deleted object deleted
使用gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)编译器所得结果如下
object created object created object deleted copy construct copy construct object created copy construct copy construct object deleted cite a copy construct call a object deleted v = 6 object deleted object deleted object deleted object deleted object deleted
可见二者的运行结果是一样的。
下面是一个C++构造函数调用的具体的例子,由于类中没有给出复制构造函数的定义导致了意想不到的问题。
strngbad.h
1 // strngbad.h -- flawed string class definition 2 #include <iostream> 3 #ifndef STRNGBAD_H_ 4 #define STRNGBAD_H_ 5 class StringBad 6 { 7 private: 8 char *str; // pointer to string 9 int len; // length of string 10 static int num_strings; // number of objects 11 12 public: 13 StringBad(const char * s); // constructor 14 StringBad(); // default constructor 15 ~StringBad(); // destructor 16 // friend function 17 friend std::ostream& operator<<(std::ostream & os, 18 const StringBad & st); 19 }; 20 #endif
strngbad.cpp
1 // strngbad.cpp -- StringBad class methods 2 #include <cstring> // string.h for some 3 #include "strngbad.h" 4 using std::cout; 5 6 // initializing static class member 7 int StringBad::num_strings = 0; 8 9 // class methods 10 11 // construct StringBad from C string 12 StringBad::StringBad(const char * s) 13 { 14 len = std::strlen(s); // set size 15 str = new char[len + 1]; // allot storage 16 std::strcpy(str, s); // initialize pointer 17 num_strings++; // set object count 18 cout << num_strings << ": "" << str 19 << "" object created "; // For Your Information 20 } 21 22 StringBad::StringBad() // default constructor 23 { 24 len = 4; 25 str = new char[4]; 26 std::strcpy(str, "C++"); // default string 27 num_strings++; 28 cout << num_strings << ": "" << str 29 << "" default object created "; // FYI 30 } 31 32 StringBad::~StringBad() // necessary destructor 33 { 34 cout << """ << str << "" object deleted, "; // FYI 35 --num_strings; // required 36 cout << num_strings << " left "; // FYI 37 delete [] str; // required 38 } 39 40 std::ostream& operator<<(std::ostream & os, const StringBad & st) 41 { 42 os << st.str; 43 return os; 44 }
vegnews.cpp
1 // vegnews.cpp -- using new and delete with classes 2 // compile with strngbad.cpp 3 #include <iostream> 4 using namespace std; 5 6 #include "strngbad.h" 7 8 void callme1(StringBad &); // pass by reference 9 void callme2(StringBad); // pass by value 10 11 int main() 12 { 13 using std::endl; 14 StringBad headline1("Celery Stalks at Midnight"); 15 StringBad headline2("Lettuce Prey"); 16 StringBad sports("Spinach Leaves Bowl for Dollars"); 17 cout << "headline1: " << headline1 << endl; 18 cout << "headline2: " << headline2 << endl; 19 cout << "sports: " << sports << endl; 20 callme1(headline1); 21 cout << "headline1: " << headline1 << endl; 22 callme2(headline2); 23 cout << "headline2: " << headline2 << endl; 24 cout << "Initialize one object to another: "; 25 StringBad sailor = sports; 26 cout << "sailor: " << sailor << endl; 27 cout << "Assign one object to another: "; 28 StringBad knot; 29 knot = headline1; 30 cout << "knot: " << knot << endl; 31 cout << "End of main() "; 32 33 return 0; 34 } 35 36 void callme1(StringBad & rsb) 37 { 38 cout << "String passed by reference: "; 39 cout << " "" << rsb << "" "; 40 } 41 42 void callme2(StringBad sb) 43 { 44 cout << "String passed by value: "; 45 cout << " "" << sb << "" "; 46 }
使用msvc2013编译器所得结果如下
1: "Celery Stalks at Midnight" object created 2: "Lettuce Prey" object created 3: "Spinach Leaves Bowl for Dollars" object created headline1: Celery Stalks at Midnight headline2: Lettuce Prey sports: Spinach Leaves Bowl for Dollars String passed by reference: "Celery Stalks at Midnight" headline1: Celery Stalks at Midnight String passed by value: "Lettuce Prey" "Lettuce Prey" object deleted, 2 left headline2: 葺葺葺葺葺葺葺葺 Initialize one object to another: sailor: Spinach Leaves Bowl for Dollars Assign one object to another: 3: "C++" default object created knot: Celery Stalks at Midnight End of main() "Celery Stalks at Midnight" object deleted, 2 left "Spinach Leaves Bowl for Dollars" object deleted, 1 left "葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺." object deleted, 0 left
使用gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)编译器所得结果如下
1: "Celery Stalks at Midnight" object created 2: "Lettuce Prey" object created 3: "Spinach Leaves Bowl for Dollars" object created headline1: Celery Stalks at Midnight headline2: Lettuce Prey sports: Spinach Leaves Bowl for Dollars String passed by reference: "Celery Stalks at Midnight" headline1: Celery Stalks at Midnight String passed by value: "Lettuce Prey" "Lettuce Prey" object deleted, 2 left headline2: Initialize one object to another: sailor: Spinach Leaves Bowl for Dollars Assign one object to another: 3: "C++" default object created knot: Celery Stalks at Midnight End of main() "Celery Stalks at Midnight" object deleted, 2 left "Spinach Leaves Bowl for Dollars" object deleted, 1 left "�v" object deleted, 0 left *** Error in `/media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg': double free or corruption (fasttop): 0x000000000076a080 *** ======= Backtrace: ========= /lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f62bf89b7e5] /lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f62bf8a437a] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f62bf8a853c] /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg[0x400cd5] /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg[0x40102c] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f62bf844830] /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg[0x400a09] ======= Memory map: ======== 00400000-00402000 r-xp 00000000 08:05 619273 /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg 00601000-00602000 r--p 00001000 08:05 619273 /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg 00602000-00603000 rw-p 00002000 08:05 619273 /media/xzgz/Code/practice/Code/C++/c++_project/CPlusGrammer/build-cpg-Desktop-Debug/cpg 00758000-0078a000 rw-p 00000000 00:00 0 [heap] 7f62b8000000-7f62b8021000 rw-p 00000000 00:00 0 7f62b8021000-7f62bc000000 ---p 00000000 00:00 0 7f62bf51b000-7f62bf623000 r-xp 00000000 08:09 2753340 /lib/x86_64-linux-gnu/libm-2.23.so 7f62bf623000-7f62bf822000 ---p 00108000 08:09 2753340 /lib/x86_64-linux-gnu/libm-2.23.so 7f62bf822000-7f62bf823000 r--p 00107000 08:09 2753340 /lib/x86_64-linux-gnu/libm-2.23.so 7f62bf823000-7f62bf824000 rw-p 00108000 08:09 2753340 /lib/x86_64-linux-gnu/libm-2.23.so 7f62bf824000-7f62bf9e4000 r-xp 00000000 08:09 2753345 /lib/x86_64-linux-gnu/libc-2.23.so 7f62bf9e4000-7f62bfbe4000 ---p 001c0000 08:09 2753345 /lib/x86_64-linux-gnu/libc-2.23.so 7f62bfbe4000-7f62bfbe8000 r--p 001c0000 08:09 2753345 /lib/x86_64-linux-gnu/libc-2.23.so 7f62bfbe8000-7f62bfbea000 rw-p 001c4000 08:09 2753345 /lib/x86_64-linux-gnu/libc-2.23.so 7f62bfbea000-7f62bfbee000 rw-p 00000000 00:00 0 7f62bfbee000-7f62bfc04000 r-xp 00000000 08:06 669579 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libgcc_s.so.1 7f62bfc04000-7f62bfe03000 ---p 00016000 08:06 669579 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libgcc_s.so.1 7f62bfe03000-7f62bfe04000 rw-p 00015000 08:06 669579 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libgcc_s.so.1 7f62bfe04000-7f62bff76000 r-xp 00000000 08:06 711828 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6 7f62bff76000-7f62c0176000 ---p 00172000 08:06 711828 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6 7f62c0176000-7f62c0180000 r--p 00172000 08:06 711828 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6 7f62c0180000-7f62c0182000 rw-p 0017c000 08:06 711828 /media/xzgz/Ubuntu/Ubuntu/Caffe/anaconda2/lib/libstdc++.so.6 7f62c0182000-7f62c0186000 rw-p 00000000 00:00 0 7f62c0186000-7f62c01ac000 r-xp 00000000 08:09 2753323 /lib/x86_64-linux-gnu/ld-2.23.so 7f62c0384000-7f62c0389000 rw-p 00000000 00:00 0 7f62c03a8000-7f62c03ab000 rw-p 00000000 00:00 0 7f62c03ab000-7f62c03ac000 r--p 00025000 08:09 2753323 /lib/x86_64-linux-gnu/ld-2.23.so 7f62c03ac000-7f62c03ad000 rw-p 00026000 08:09 2753323 /lib/x86_64-linux-gnu/ld-2.23.so 7f62c03ad000-7f62c03ae000 rw-p 00000000 00:00 0 7ffece974000-7ffece995000 rw-p 00000000 00:00 0 [stack] 7ffece9a0000-7ffece9a2000 r--p 00000000 00:00 0 [vvar] 7ffece9a2000-7ffece9a4000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
需要注意的是,在StringBad类中,static int num_strings是静态成员变量,它由该类实例化的所有对象所共享,不能在类声明中初始化静态成员变量,可以在类声明之外使用单独的语句来进行初始化。