【C++编程】1. 拷贝控制、赋值和销毁

拷贝控制、赋值和销毁

  • 如果一个构造函数的第一个参数是自身类的引用,且额外的参数都有默认值,则此构造函数是拷贝控制函数(拷贝构造函数不应该是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;

 

posted @ 2018-05-03 20:21  苏格拉底的落泪  阅读(319)  评论(0编辑  收藏  举报