程序猿c++(6) 拷贝构造函数

  • A+
所属分类:C++

代码举例

#include
using namespace std;
static int objectCount = 0;
class CExample
{
public:
    //构造函数
    CExample()
    {
        objectCount++;
        print("constructor is called\n");
    }
    //拷贝构造函数//使用引用,不可通过引用修改变量的值
    CExample(const CExample & c)
    {
        objectCount++;
        print("copy constructor is called\n");
    }
    //析构函数
    ~CExample()
    {
     objectCount--;
     print("destructor is called\n");
    }

    void print(const string& msg = "")
    {
     if(msg.size()!=0) cout<<msg<<":";
     cout<<"objectCount = "<<objectCount <<endl;
    }
    
};
CExample f(CExample c)
{
    cout<<"begin of f"<<endl;
    c.print("x argument inside f()");
    cout<<"end of f"<<endl;
    return c;
}
int main()
{
    CExample h;
    h.print("after construction of h");
    CExample h2 = f(h);
    h.print("after call to f()")
    return 0;
}
运行结果:
constructor is called:objectCount = 1
after construction of h:objectCount = 1
copy constructor is called:objectCount = 2
begin of f
x argument inside f():objectCount = 2
end of f
copy constructor is called:objectCount = 3
destructor is called:objectCount = 2
after call to f()
destructor is called:objectCount = 1
destructor is called:objectCount = 0

过程解析:

CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。如果我们不定义这个拷贝构造函数,那么编译器会自动生成该拷贝构造函数。

运行CExample h2 = f(h)时候

进入f()函数前:

  1. 生成临时变量c
  2. 调用拷贝构造函数将h赋给c

return返回时候:

  1. 先会产生一个临时变量,就叫XXXX吧
  2. 然后调用拷贝构造函数把c的值给XXXX。整个这两个步骤有点像:CExample XXXX(c)
  3. 在函数执行到最后先析构c局部变量
  4. 等main()执行完后再析构掉XXXX对象
  5. ps:这里的临时变量的拷贝构造过程可能因为编译器优化而去掉,所以上面程序在f(h)return的时候,其实只是用f(h)结果初始化h2的时候发生了拷贝构造,return过程的拷贝构造被去掉了。

系统默认的拷贝构造函数会拷贝对象的每一个成员变量,如果成员变量是int,将会把一个该值拷贝给一个int变量;如果成员变量是一个对象,那么会调用该类的拷贝构造函数来拷贝数据,所以如果成员变量是对象,这种操作会递归下去。拷贝构造是成员级别的拷贝,而不是字节级别的,如果拷贝中只有基本类型,没有对象,那么,过程其实就是字节拷贝。

如果成员里面有指针,那么就会拷贝指针,两个指针将会指向同一个内存。

默认拷贝构造函数执行的是浅拷贝

深拷贝和浅拷贝

程序猿c++(6) 拷贝构造函数

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了。深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。这里,Person在进行拷贝构造的时候,name指针只会进行浅拷贝,因为指针其实就是int类型的数据,是个简单的int复制操作,那么两个Person对象的name将会指向同一个内存,如下图左;如果对内存进行析构,将会对同一个内存析构两次而报错。因此,我们需要深拷贝,如下图右,两个Person对象的name指向两个不同的内存,内存中存储的值一样。

程序猿c++(6) 拷贝构造函数

如果改成深拷贝,则需要重写拷贝构造函数,为拷贝对象的name重新分配内存空间,并将被拷贝对象的name值赋给拷贝对象的name。

程序猿c++(6) 拷贝构造函数

什么时候发生拷贝构造

(1)函数调用时,传实参给形参

程序猿c++(6) 拷贝构造函数

(2)初始化

程序猿c++(6) 拷贝构造函数

(3)函数return的时候,但这个时候并不一定会发生,编译器会出于效率对不必要的拷贝构造过程进行去除。

建议

每写一个类,都自己编写拷贝构造函数、构造函数、析构函数

如果你不希望你的对象被拷贝,那你可以把拷贝构造函数声明为private

  • 我的微信
  • weinxin
  • 微信公众号
  • weinxin
阿拉灯aladeng

发表评论

您必须才能发表评论!