C++拷贝构造函数

本文主要参考:
http://blog.csdn.net/lwbeyond/article/details/6202256

拷贝函数的定义

拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
对于一个类X, 如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.
(PS:volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问)

拷贝构造函数举例:

#include <iostream>  
using namespace std;  

class CExample {  
private:  
    int a;  
public:  
    //构造函数  
    CExample(int b)  
    { a = b;}  

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

    //一般函数  
    void Show ()  
    {  
        cout<<a<<endl;  
    }  
};  

int main()  
{  
    CExample A(100);  
    CExample B = A; // CExample B(A); 也是一样的  
     B.Show ();  
    return 0;  
}  

拷贝构造函数的调用时机

  • 对象以值传递的方式传入函数参数
    class CExample   
    {  
    private:  
     int a;  

    public:  
     //构造函数  
     CExample(int b)  
     {   
      a = b;  
      cout<<"creat: "<<a<<endl;  
     }  

     //拷贝构造  
     CExample(const CExample& C)  
     {  
      a = C.a;  
      cout<<"copy"<<endl;  
     }  

     //析构函数  
     ~CExample()  
     {  
      cout<< "delete: "<<a<<endl;  
     }  

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

    //全局函数,传入的是对象  
    void g_Fun(CExample C)  
    {  
     cout<<"test"<<endl;  
    }  

    int main()  
    {  
     CExample test(1);  
     //传入对象  
     g_Fun(test);  

     return 0;  
    }  

调用g_Fun(test)时,主要发生了三件事:
1.test赋值给形参时,会产生一个临时变量tmp;
2.然后会调用拷贝构造函数,将test的值赋值给tmp;
3.等g_Fun(test)执行完,就会将tmp析构。

  • 对象以值传递的方式从函数返回
    class CExample   
    {  
    private:  
     int a;  

    public:  
     //构造函数  
     CExample(int b)  
     {   
      a = b;  
     }  

     //拷贝构造  
     CExample(const CExample& C)  
     {  
      a = C.a;  
      cout<<"copy"<<endl;  
     }  

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

    //全局函数  
    CExample g_Fun()  
    {  
     CExample temp(0);  
     return temp;  
    }  

    int main()  
    {  
     g_Fun();  
     return 0;  
    }  

g_Fun() 执行到return时,主要发生如下几件事:
1.首先会产生一个局部变量xxx;
2.然后会将tmp的值赋值给xxx,此时有点类似CExample xxx(tmp)代码片;
3.紧接着就会析构掉tmp变量;
4.等g_Fun() 执行完毕,就会析构掉xxx变量。

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

浅拷贝与深拷贝

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了。在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间。
example1:

    class Rect  
    {  
    public:  
        Rect()      // 构造函数,计数器加1  
        {  
            count++;  
        }  
        ~Rect()     // 析构函数,计数器减1  
        {  
            count--;  
        }  
        static int getCount()       // 返回计数器的值  
        {  
            return count;  
        }  
    private:  
        int width;  
        int height;  
        static int count;       // 一静态成员做为计数器  
    };  

    int Rect::count = 0;        // 初始化计数器  

    int main()  
    {  
        Rect rect1;  
        cout<<"The count of Rect: "<<Rect::getCount()<<endl;  

        Rect rect2(rect1);   // 使用rect1复制rect2,此时应该有两个对象  
         cout<<"The count of Rect: "<<Rect::getCount()<<endl;  

        return 0;  
    }  

由于Rect rect2(rect1);调用的是拷贝构造函数,所以静态计数器变量count并没有实行+1操作,程序结束后,存在两个对象,将实行两次析构函数的调用,所以最后静态计数器变量count将变成负数。

example2:

class Rect  
{  
public:  
    Rect()      // 构造函数,p指向堆中分配的一空间  
    {  
        p = new int(100);  
    }  
    ~Rect()     // 析构函数,释放动态分配的空间  
    {  
        if(p != NULL)  
        {  
            delete p;  
        }  
    }  
private:  
    int width;  
    int height;  
    int *p;     // 一指针成员  
};  

int main()  
{  
    Rect rect1;  
    Rect rect2(rect1);   // 复制对象  
    return 0;  
} 

这里编译会报错。主要是因为在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:
这里写图片描述
在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p = rect2.p,也即这两个指针指向了堆里的同一个空间,如下图所示:
这里写图片描述
当程序执行完毕,将对同一内存地址进行两次释放,这就是编译报错的原因。

解决上面的问题,就是编写拷贝构造函数,在里面动态赋值:

class Rect  
{  
public:  
    Rect()      // 构造函数,p指向堆中分配的一空间  
    {  
        p = new int(100);  
    }  
    Rect(const Rect& r)  
    {  
        width = r.width;  
        height = r.height;  
        p = new int;    // 为新对象重新动态分配空间  
        *p = *(r.p);  
    }  
    ~Rect()     // 析构函数,释放动态分配的空间  
    {  
        if(p != NULL)  
        {  
            delete p;  
        }  
    }  
private:  
    int width;  
    int height;  
    int *p;     // 一指针成员  
}; 

这里写图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值