一、什么是复制控制成员

在类中特有的三个成员函数统称为复制控制成员,它们是:复制构造函数、赋值操作符函数、析构函数

  • 复制构造函数,此复制构造函数非我们熟知的构造函数。需要以一个对象为模板创建另一个对象时自动调用。
  • 赋值操作符函数,这是对"操作符="的重载函数,把一个对象的内容赋值给另一个对象时自动调用
  • 析构函数,在释放对象时自动调用,与构造函数相对应

下文会分别针对这三个函数做详细说明。

二、复制控制成员的特点

相同点

把这三个成员函数归为一类函数,是因为它们有许多相同点:

  • 每个类都有这三函数,如果作者没有显示地写出来,编译器会提供默认的
  • 它们只能修改类的非static成员,对于static成员,只能读不能写
  • 特殊情况下(具体指针类型的成员变量时),通常需要自己重载这三个函数
  • 当你发现需要自己定义其中一个函数时,通常意味着你还要重载另外两个函数
  • 它们都被编译器默默地调用的
    不同点
  • 赋值操作符与复制构造的区别,只是不用为对象开辟新空间
  • 即使自己定义了析构函数,系统还是会执行默认的析构函数,顺序是先运行自己的析构函数,再运行默认的析构函数

复制构造函数

详见:复制构造函数总结

赋值操作符函数

赋值操作符也是依次复制每个非static成员,相当于memcpy。
赋值操作符与复制构造的区别,只是不用为对象开辟新空间

析构函数

1.成员被析构的顺序

成员被析构的顺序与成员被创建对象的逆序,也即声明次序的逆序
例:

class A  
{  
    int x;  
public:  
    A(int i){x = i;cout<<"construct A "<<i<<endl;}  
    ~A(){cout<<"delete A "<<x<<endl;}  
};  
class B  
{  
    A a;  
    A b;  
    A c;  
public:  
    B():c(3),b(2),a(1){cout<<"construct B"<<endl;}  
    ~B(){cout<<"delete B"<<endl;}  
};  
int main()  
{  
    B b;  
    return 0;  
}

输出结果:

construct A 1
construct A 2
construct A 3
construct B
delete B
delete A 3
delete A 2
delete A 1

这里特意让初始化列表的顺序不同,可以看出构造与析构的顺序与初始化列表无关,只与声明顺序有关

2.和其它函数不同的是,即使定义了自己的析构函数,还是会执行默认的析构函数

先运行自己的析构函数,再运行默认的析构函数

为什么要重载复制控制成员?

深层复制 vs 浅层复制

通常情况下,一个对象占据一段连续的内存空间。
对象的复制只指把这一段空间的数据复制到另一段空间。
特殊情况下,对象的数据包含另一段空间的地址,那么被指向的那一段空间是否属于这个对象呢?是否有必要复制这一段空间呢?
浅层复制的作法是只复制对象占据的空间,不复制对象指向的空间。那么复制后的对象与原对象对指向同一空间。
深层复制的作法是把对象占据的空间和对象指向的空间都复制一遍,复制后两个对象不太有关联。

复制控制成员

在复制控制函数中,复制时只复制内容,这种复制称为浅层复制

比如复制指针,只复制指针中的地址,不复制指针指向的内容。

当类中有指针成员时,浅层复制会出错。需要定义自己的深层复制

如果需要定义自己的析构函数,就肯定需要定义自己的所有这也三个控制成员函数

定义自己的控制成员函数

class A  
{  
    int val;  
    int *ptr;  
public:  
    /*构造函数 
    ptr(new int(p)):创建一个int,int的内容与p相同,ptr指向该int*/  
    A(const int &p, int i):ptr(new int(p)),val(i){}  
    /*复制构造函数 
    *orig.ptr:取指针orig.ptr指向的内容 
    ptr(new int(*orig.ptr)):创建一个int,int的内容与*orig.ptr相同,ptr指向该int*/  
    A(const A &orig):ptr(new int(*orig.ptr)),val(orig.val){}  
    /*赋值操作符不需要开辟空间*/  
    A& operator=(const A &orig)  
    {  
        /*是函数内容的意思 
        rig.ptr指向的内容赋给ptr指向的内容*/  
        *ptr = *orig.ptr;  
        val = orig.val;  
        return *this;  
    }  
    //析构函数  
    ~A(){delete ptr;}  
};

需要注意的是,在本例中,this对象可以访问orig对象的私有成员。

对于私有的定义是这样的:只允许类的创建者(在这里是this)和该类的成员函数可以访问

我理解的可以访问是指可以访问创建者的私有成员。

为什么在本例中this对象可以访问orig对象的私有成员?求解释

notes

如果类中有指针成员,复制控制函数一定要自己写。

有时候,复制控制的应用不是很明显,容易被忽略。

所以发现类中有指针成员时,一定要仔细分析类的使用,不能忽视任意一处可能的复制控制的使用

最保险的方法就是,只要看到类中有指针成员,就自己写复制控制

results matching ""

    No results matching ""