C++拷贝构造函数、赋值构造函数(深拷贝,浅拷贝)

博客介绍了拷贝构造函数,它是特殊构造函数,可阻止编译器形成默认拷贝构造函数。还讲解了赋值构造函数,涉及对象赋值操作及重载“=”的必要性。此外,指出浅拷贝拷贝地址,在类内成员需动态开辟堆内存时会出现野指针问题,深拷贝则会重新分配资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,拷贝构造函数

拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(const X& x)。自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

对于普通类型的对象,它们之间的复制是很简单的,例如:
int a=88;
int b=a; 
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。

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


class Test{
private:
    int a;

public:
    Test(int b){
        a=b;
    }

    void excute(){
        cout<<a<<endl;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Test A(100);
    Test B=A;
    B.excute();
    return a.exec();
}

运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象B分配了内存并完成了与对象A的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。下面举例说明拷贝构造函数的工作过程。

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


class Test{
private:
    int a;

public:
    Test(int b){
        a=b;
    }

    Test(const Test&C){//拷贝构造函数
        a=C.a;
    }

    void excute(){
        cout<<a<<endl;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Test A(100);
    Test B=A;
    B.excute();
    return a.exec();
}

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:

  • 当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调用拷贝构造函数
    BOOL testfunc(CExample obj);
    testfunc(theObjone); //对象直接作为参数。
    BOOL testfunc(CExample obj)
    {
    //针对obj的操作实际上是针对复制后的临时拷贝进行的
    }
  • 当函数中的局部对象作为返回值被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用。

CTest func()  
{  
   CTest   theTest;  
   return   theTest  
}   

  • 一个对象需要通过另外一个对象进行初始化。
    CExample A(100); 
    CExample B = A; 

     

如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝。

二,赋值构造函数

iint main(int argc, char * argv[])  
{  
     CExample A;  
     A.Init(40);  
      
     CExample C;  
     C.Init(60);  
  
     //现在需要一个对象赋值操作,被赋值对象的原内容被清除,并用右边对象的内容填充。  
     C = A;  
    return 0;  
}

 用到了"="号,但与上面的例子中语句“ CExample B=A;  ” 不同“ CExample B=A;  ”语句中的 "=" 在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号表示。 例CExample B(A); 

而本例子中,"=" 表示赋值操作。将对象 A 的内容复制到对象C;,这其中涉及到对象C 原有内容的丢弃,新内容的复制。 但"="的缺省操作只是将成员变量的值相应复制。旧的值被自然丢弃。 由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放。指针的值被复制了,但指针所指内容并未复制。 因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载"="赋值操作符号。 

注意:拷贝构造函数是在对象被创建时调用的,而赋值构造函数只能被已经存在了的对象调用

String a(“hello”);
String b(“world”);
String c = a; // 调用了拷贝构造函数,最好写成 c(a);
c = b; // 调用了赋值构造函数

三,浅拷贝的问题

            浅拷贝也叫位拷贝,拷贝的是地址。

            深拷贝也叫值拷贝, 拷贝的是内容。

在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

  深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。

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

class Test{
private:
    int a;
    char *str;

public:
    Test(int b,const char* _str){//构造函数
        a=b;
        str=new char[b];
        strcpy(str,_str);
    }

    Test(const Test&C){//拷贝构造函数
        a=C.a;
        str = new char[a];//深拷贝
        if(str!=0)
            strcpy(str,C.str);
    }

    Test& operator =(const Test& D){//赋值构造函数
        a=D.a;//复制常规成员
        char *temp = new char[a];//复制指针指向的内容
        strcpy(temp,D.str);

        delete []str;//删除原指针内容
        str=temp;//建立新的指向
        return *this;
    }

    void excute(){
        cout<<str<<endl;
    }
    ~Test(){//析构函数
        delete str;
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    //拷贝构造函数测试
    Test A(20,"hello!");
    Test B=A;
    B.excute();
    
    //赋值构造函数测试
    Test C(30,"I love you!");
    Test D(30,"I hate you!");
    C=D;
    C.excute();
    return a.exec();
}

      参考:https://www.cnblogs.com/BlueTzar/articles/1223313.html

https://www.cnblogs.com/nigang/p/3613452.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土拨鼠不是老鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值