拷贝控制、赋值和销毁
- 如果一个构造函数的第一个参数是自身类的引用,且额外的参数都有默认值,则此构造函数是拷贝控制函数(拷贝构造函数不应该是explicit的)。
- 如果我们没有为一个类定义拷贝构造函数,编译器会为我们定义一个,与合成默认构造函数不同, 即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数。
1 class Sales_data 2 { 3 public: 4 Sales_data(const Sales_data&); 5 private: 6 string bookNo; 7 int units_sold = 0; 8 double revenue = 0.0; 9 }; 10 11 Sales_data::Sales_data(const Sales_data &rhs): 12 bookNo(rhs.bookNo), //ISBN号 13 units_sold(rhs.units_sold), //销量 14 revenue(rhs.revenue) //总销售收入 15 {}
析构函数
- 当一个类定义自己的析构函数时,编译器会为它定义一个合成析构函数。
练习参考答案
13.13
1 #include<iostream> 2 #include<string> 3 #include<vector> 4 using namespace std; 5 6 struct X 7 { 8 X() { cout << "默认构造函数X()" << endl; } 9 X(const X&) { cout << "拷贝构造函数 X(const X&)" << endl; } 10 X& operator=(const X &rhs) { cout << "拷贝赋值运算符=(const X&)" << endl; return *this; } 11 ~X() { cout << "析构函数 ~()" << endl; } 12 }; 13 14 void f1(X x) 15 {} 16 17 void f2(X &x) 18 {} 19 20 int main() 21 { 22 cout << "局部变量" << endl; 23 X x; 24 cout << endl; 25 26 cout << "非引用参数传递:" << endl; 27 f1(x); 28 cout << endl; 29 30 cout << "引用参数传递" << endl; 31 f2(x); 32 cout << endl; 33 34 cout << "动态内存:" << endl; 35 X *px = new X(x); 36 cout << endl; 37 38 cout << "添加到容器中:" << endl; 39 vector<X> vx; 40 vx.push_back(x); 41 cout << endl; 42 43 cout << "释放动态内存" << endl; 44 delete px; 45 cout << endl; 46 47 cout << "间接初始化和赋值:" << endl; 48 X y = x; 49 y = x; 50 cout << endl; 51 52 cout << "程序结束" << endl; 53 return 0; 54 }
运行结果:
三五法则
- 如果一个类需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。
- 如果一个人类需要一个拷贝构造函数,几乎可以肯定它也需要一个拷贝赋值运算符。
- 如果一个类需要一个拷贝赋值运算符,几乎可以肯定它也需要一个拷贝构造函数。
练习参考答案
//13.14: 0 0 0 //13.15: 3 4 5 //13.16: 0 1 2 #include<iostream> using namespace std; class numbered { private: static int seq; public: numbered() { mysn = seq++; } numbered(numbered &n) { mysn = seq++; } int mysn; }; int numbered::seq = 0; void f(numbered s) { cout << s.mysn << endl; } int main(int argc, char **argv) { numbered a, b = a, c = b; f(a); f(b); f(c); return 0; }
使用=default
- 我们可以通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本。
class Sales_data { public: //拷贝控制成员; 使用default Sales_data() = default; Sales_data(const Sales_data &) = default; Sales_data& operator=(const Sales_data &); ~Sales_data() {} = default; //其他成员的定义,如前 } Sales_data& Sales_data::operator=(const Sales_data &) = default;
阻止拷贝
如果一个类有数据成员不能默认构造、拷贝、复制和销毁,则对应的成员函数将被定义为删除的。规则引申如下:
- 如果类的某个成员的析构函数时删除的或不可访问的,则类的合成析构函数被定义为删除的;同时类的默认构造函数是删除的;同时类的合成拷贝构造函数也被定义为删除的。(析构函数被定义为删除的,则不能定义该类型的变量。)
- 如果类的某个成员的拷贝构造函数是删除的或不可访问的,则类的合成拷贝构造函数被定义为删除的;
- 如果类的某个成员的拷贝赋值运算符是删除的或不可访问的,或类有一个const的或引用成员,则类的合成拷贝赋值运算符被定义为删除的;
- 如果类有一个引用成员,它没有类内初始化器,或是类有一个const成员,没有类内初始化器且其类型未显式定义默认构造函数,则该类的默认构造函数是删除的。
练习参考答案
【练习13.5】
1 class HasPtr 2 { 3 public: 4 HasPtr(const string &s = string()): ps(new string(s)), i(0) {} 5 HasPtr(const HasPtr &rhs): ps( new string(*rhs.ps)), i(rhs.i) {} 6 private: 7 string *ps; 8 int i; 9 }
【练习13.8】
1 class HasPtr 2 { 3 public: 4 HasPtr(const string &s = string()): ps(new string(s)), i(0) {} 5 HasPtr(const HasPtr &rhs): ps( new string(*rhs.ps)), i(rhs.i) {} 6 7 HasPtr operator=(const HasPtr &rhs); 8 private: 9 string *ps; 10 int i; 11 } 12 13 HasPtr HasPtr::operator=(const HasPtr &rhs) 14 { 15 ps = new string(*rhs.ps); 16 i = rhs.i; 17 return *this;