拷贝构造 赋值构造 浅拷贝 深拷贝

拷贝构造函数

拷贝构造是在传入一个类的实例作为参数创建新的类的时候触发的

class ctest
{
public:
    ctest(ctest& t)
    {
        cout << "ctest(ctest& t)" << endl;
    }
};

也可以为参数增加const修饰防止修改。触发拷贝构造的情况一般有两种

ctest a;
ctest b(a);
ctest c = a;

b和c都会调用拷贝构造,b很好理解,直接传递了一个实例a,所以从函数的参数列表对应,也是应该调用拷贝构造。对于c可能有人认为是赋值构造,实际上是错误的,因为赋值的时候c是没创建的,相当于用a初始化创建c,所以还是拷贝构造。并且赋值构造并不能算作构造函数,它有返回值,赋值构造可以叫做赋值函数。

注意

拷贝构造函数必须是传入的引用或者指针也可以,但是不可以传递形参,现在的编译器已经可以直接识别错误了。因为如果传递形参,那么在形参传递过程种需要创建局部变量,那么又会调用赋值构造,就会导致死循环创建。

赋值构造

赋值构造相当于重载了操作符等号(=)

class ctest
{
public:
    ctest& operator = (ctest& t)
    {
        return *this;
    }
};

赋值构造函数必须是返回自己的引用,因为有连等的操作,可以不返回,但是会在连等的情况下出问题,按照C++的等号操作符的规则,是需要返回自己的引用

赋值构造在已存在的实例赋值操作时触发,如果实例是在创建的时候调用等号,就会调用上面的拷贝构造

ctest a;
ctest b;
b = a;

注意与上面的示例区别

浅拷贝

对于有指针类型成员的类,拷贝构造和赋值构造的时候需要注意,因为如果我们不重载拷贝构造或者赋值构造,类是有默认的函数的,它的操作就是直接赋值,那么就会导致两个类的指针保存了同一个值,也就是指向了同一块空间,这样不管在析构还是在使用的时候都会引发问题

class ctest
{
public:
    ctest()
    {
        m_data = NULL;
    }
    ~ctest()
    {
        if (m_data != NULL)
        {
            delete[] m_data;
            m_data = NULL;
        }
    }
    char* m_data;
};
void test()
{
    ctest a;
    a.m_data = new char[10]{ 'a','b' };

    //下面这两个类都会在析构的时候报错
    ctest b(a);
    ctest c;
    c = a;
}
int main()
{
    test();
    return 0;
}

在test函数种,类a申请了内存,并且做了初始化,然而b和c都在构造和赋值的时候让自己内部的m_data值等于了a的m_data。那么指向同一块内存的多个指针在析构的时候进行多次释放,导致崩溃。这种情况下需要深拷贝

深拷贝

深拷贝就是在拷贝和复制的时候进行一个额外的操作,防止出现多个类实例公用一块空间而导致不可预料的问题

class ctest
{
public:
    ctest()
    {
        m_data = NULL;
    }
    ctest(const char * p)
    {
        if (p != NULL)
        {
            m_data = new char[strlen(p) + 1]();
            strcpy(m_data, p);
        }
    }
    ctest(const ctest& t)
    {
        if (t.m_data != NULL)
        {
            m_data = new char[strlen(t.m_data) + 1]();
            strcpy(m_data, t.m_data);
        }
    }
    ctest& operator=(const ctest& t)
    {
        if (this != &t && t.m_data != NULL)
        {
            m_data = new char[strlen(t.m_data) + 1]();
            strcpy(m_data, t.m_data);
        }
        return *this;
    }
    ~ctest()
    {
        if (m_data != NULL)
        {
            delete[] m_data;
            m_data = NULL;
        }
    }
private:
    char* m_data;
};
void test()
{
    ctest a("abc");
    ctest b(a);
    ctest c;
    c = a;
}
int main()
{
    test();
    return 0;
}

上面就是实现了一个简单的string类,其中就用到了深拷贝,在构造和赋值的时候,都会为自己的类重新申请一块空间保存传递进来参数的数据,而不是直接赋值,这样就可以避免在析构的时候对同一块内存多次释放的问题。

版权声明:本文版权归作者所有,如需转载,请标明出处

原文地址:https://www.cnblogs.com/studywithallofyou/p/14557966.html