c++OOP之复制控制 ------复制构造函数、赋值重载、析构

本博文我们讨论OOP复制控制的一些内容;

首先考虑对象复制的时机: 非引用类型

1):根据一个类去显式或者隐式初始化一个对象;

2):复制一个对象,将它作为实参传给一个函数;

3):从函数返回时复制一个对象。( string    tolittle(string word))

4):初始化顺序容器中的元素。(例如vector 必须具备 copy,assignment功能)

一个空类,编译器提供默认无参数构造函数、拷贝构造函数、赋值运算符以及析构函数,一共四个函数。(面试)!!
11.复制构造函数、赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个。以String为例:
a)  涉及到深拷贝、浅拷贝问题,所以需要提供拷贝构造函数
b)  然后,为了保持一致,赋值运算符也应该实现深拷贝
c)  既然实现深拷贝,那么必定申请了资源(例如内存),所以必然需要析构函数来手工释放。

 一:复制构造函数:

复制构造函数调用的时机就是在对象复制的时候。方式有两种:

1):当用户不提供时,编译器自动为我们合成一个拷贝构造函数;

示例代码如下:

#include <iostream>
#include <string>
using namespace std;

class Student 
{
    public:
        Student()
        {}
        Student(int id, const string&name, int age)
            :id_(id),name_(name),age_(age)
        {}
        
        void print()
        {  cout << id_ <<":" << name_ <<":" << age_ << endl; }
    private:
        int id_;
        string name_;
        int age_;
};

int main(int argc, const char *argv[])
{
    Student s(11,"zhangsan",23);
    s.print();

    Student s2(s); //执行此句,调用默认复制构造函数
    s2.print();
    return 0;
}

 2):用户自己定义:

放入class Student 的public中即可;

1   Student(const Student &s)
2         {
3             id_ = s.id_;
4             name_ = s.name_;
5             age_ = s.age_;
6         }

复制构造函数之 深copy 和 浅copy;

含有指针成员变量的类在复制时,有两种选择:
a) 复制指针的值,这样复制完毕后,两个对象指向同一块资源,这叫做 浅拷贝shallow copy(有可能出错)
b) 复制指针所指向的资源,复制完毕后,两个对象各自拥有自己的资源,这叫做 深拷贝 deep copy

 示例代码:

 1 #include "iostream"
 2 #include "string.h"
 3 using namespace std;
 4 
 5 //copy String 时,仅仅复制指针的值
 6 //导致析构时发生了问题
 7 class String
 8 {
 9     public:
10         String();
11         String(const char*s);
12         String(const String &s);
13         ~String();       
14         void print()const
15         {
16             cout << str_ << endl;
17         }
18     private:
19         char *str_;
20 };
21 String::String()
22     :str_(new char(0))
23 {  }
24 String::String(const char *s)
25     :str_(new char[strlen(s)] +1)
26 {
27     ::strcpy(str_, s);
28 }
29 //程序结束时会调用析构函数,两个对象s1,s2必然会析构两次,而str_指向同一片区域;
30 //当析构s1时,s1.str_所指向的区域被释放;而s2.str_ 也同时被释放
31 //当析构s2时,由于s2.str_已经被s1.str_释放,所以会产生错误.
32 String::String(const String &s)
33     :str_(s.str_)
34 {}
35 
36 /* 修改成以下即可:
37  *String::String(const String &s)
38  *     :str_(new char[strlen(s)] +1)
39  * {
40  *  ::strcpy(str_, s.str_);
41  *  }
42  */
43 String::~String()
44 {
45     delete[] str_; //注意
46 }
47 int main(int argc, const char *argv[])
48 {
49     String s("helloworld");
50     s.print();
51     
52     String s2(s);
53     s.print();
54     return 0;
55 }

二: 赋值运算符的重载:【因为我们自定义的类没有内置比较运算符(<、>、!=)】
示例代码如下:

 1 #include <iostream>
 2 #include <string.h>
 3 using namespace std;
 4 
 5 class String
 6 {
 7     public:
 8         String();
 9         String(const char *s);
10         String(const String &s);
11         ~String();
12         String &operator = (const String &s);
13         size_t size()const //自己构造size函数
14         {
15             return strlen(str_);
16         }
17         
18         void print()
19         { cout << str_ << endl; }
20     private:
21        char *str_;
22 };
23 String::String()
24     :str_(new char[1])
25 { *str_ = 0; }
26 
27 String::String(const char*s)
28     :str_(new char(strlen(s)+1))
29 { ::strcpy(str_, s); }
30 
31 String::String(const String &s)
32     :str_(new char(strlen(s.str_)+1))
33 { ::strcpy(str_, s.str_); }
34 
35 String &String::operator = (const String &s)
36 {
37    if(this == &s)//自身赋值
38         return *this;
39     delete[]str_;
40     str_ = new char(s.size() + 1 );
41     ::strcpy(str_, s.str_);
42     
43     return *this;
44 }
45 
46 String::~String()
47 { delete[] str_; }
48 
49 int main(int argc, const char *argv[])
50 {
51     String s("helle");
52     s.print();
53     
54     String s2;
55     s2 = s; //赋值重载
56     s2.print();
57     
58     s2 = s2 ; //注意自身赋值情形
59     s2.print();
60     
61     return 0;
62 }

禁止类复制和赋值的方法:

a):把 copy构造函数和复制运算符设为private;

b):只声明,不实现;

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 using namespace std;
 5 
 6 //no copy,no assignment
 7 //google 推荐写法
 8 #define   DISALLOW_COPY_AND_ASSIGN(TypeName) 
 9             TypeName(const TypeName&); 
10                 void operator=(const TypeName&)
11 
12 class Test
13 {
14     public:
15         Test(){}
16         ~Test(){}
17 
18     private:
19         Test(const Test&t);
20         void operator=(const Test &t);
21 };
22 
23 int main(int argc, const char *argv[])
24 {
25     Test t;
26     
27     Test t2(t);//error
28 
29     Test t3;
30     t3 = t; //error
31     
32     return 0;
33 }
a):如果一个类,不需要复制和赋值,那就禁用这种能力,这可以帮助避免大量潜在的bug。
b):如果一个类,实现了像value一样的复制和赋值能力(意味着复制和赋值后,
两个对象没有任何关联,或者逻辑上看起来无任何关联),那么就称这个类的对象为  值语义(value semantics)。
如果类不能复制,或者复制后对象之间的资源归属纠缠不清,那么称为 对象语义(object semantics),或者 引用语义(reference semantics)。
原文地址:https://www.cnblogs.com/xfxu/p/3994038.html