C++基础---浅层及深层拷贝构造函数

1. 浅层及深层拷贝构造函数

1.1 拷贝构造函数的定义

  • 拷贝构造函数:又称复制构造函数,是一种特殊的构造函数;
  • 拷贝构造函数:由编译器调用来完成一些基于同一类的其他对象的构建及初始化
  • 拷贝构造函数:唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制;
  • 拷贝构造函数:经常用在函数调用时用户定义类型的值传递及返回;
  • 拷贝构造函数:要调用基类的拷贝构造函数和成员函数;
  • 拷贝构造函数:如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用;
  • 拷贝构造函数:是由普通构造函数和赋值操作符共同实现的;

1.2 拷贝构造函数的调用情形

  • 在C++中,下面三种对象需要调用拷贝构造函数:
    (1)一个对象作为函数参数,以值传递的方式传入函数体;
    (2)一个对象作为函数返回值,以值传递的方式从函数返回;
    (3)一个对象用于给另外一个对象进行初始化(常称为赋值初始化);

1.3 默认拷贝构造函数

  • 编译器提供了默认构造函数和析构函数,编译器同样也提供了默认拷贝构造函数
  • 但编译器提供的默认拷贝构造函数只是把传递进来的对象的每个成员复制到新对象的成员变量中去
  • 两个对象中的变量均指向存放类成员变量的那块内存区域
  • 定义形式:A(const A &a);

1.4 浅层拷贝构造函数

  • 由于编译器提供的默认拷贝构造函数只是把传递进来的对象的每个成员复制到新对象的成员变量中去。
  • 两个对象中的变量均指向存放类成员变量的那块内存区域,我们把这种拷贝叫浅层拷贝。
  • 假如该变量不是指针变量,一般不会立即出错;
  • 假如该变量是指针变量时,当这两个对象中的任何一个超出作用域时,都会出现知名的错误,产生迷途指针。
  • 比如,将对象a删除,那么析构函数会自动释放堆中存放的那块内存空间,而对象b的指针变量x仍指向该内存空间,由于该空间已经不在,那么对象b的指针变量x现在就变成了一个迷途指针,该程序处于崩溃边缘。
  • 代码示例:

    
    #include <iostream> 
    
    using namespace std;
    class A
    {
    public:
        A()
        {
            x = new int;
            *x = 5;
        }
        ~A()
        {
            delete x;
            x=NULL;
        }
        A(const A &a)
        {
            cout<<"拷贝构造函数执行..."<<endl;
            x = a.x;
        }
        int print()const
        {
            return *x;
        }
        void set(int i)
        {
            *x = i;
        }
    private:
        int *x;
    };
    int main()
    {
        A *a = new A();
        cout<<"a: "<<a->print()<<endl;
        A b = (*a);//调用拷贝构造函数
        a->set(32);
        cout<<"b: "<<b.print()<<endl;
        b.set(99);
        cout<<"a: "<<a->print()<<endl;
        //delete a;
        system("pause");
        return 0;
    }
    =>a:5
      拷贝构造函数执行...
      b:32
      a:99

    注:假如将上述例子中的delete a;语句打开注释,程序立马崩溃,对象b中的指针成员变量x仍然指向已经释放的内存空间,对象b中的指针成员变量x成了迷途指针。

1.5 深层拷贝构造函数

  • 为了解决浅层拷贝导致的迷途指针的问题,必须创建自己的拷贝构造函数,并且在函数中为成员变量分配内存
  • 这样,在分配完内存后,旧对象的成员变量就可以复制到新的内存区域中。
  • 两个对象的成员变量都各自拥有自己的内存区域
  • 代码示例:

    
    #include <iostream> 
    
    using namespace std;
    class A
    {
    public:
        A()
        {
            x = new int;
            *x = 5;
        }
        ~A()
        {
            delete x;
            x=NULL;
        }
        A(const A &a)
        {
            cout<<"拷贝构造函数执行..."<<endl;
            //x = a.x;
            x = new int;//开辟新的空间,复制构造对象的同时将成员指针变量保存在新空间中
            *x = *(a.x);//读取旧对象的成员指针变量x地址处的数据并赋值给新对象的成员指针变量x所指向的内存区域
        }
        int print()const
        {
            return *x;
        }
        void set(int i)
        {
            *x = i;
        }
    private:
        int *x;
    };
    int main()
    {
        A *a = new A();
        cout<<"a: "<<a->print()<<endl;
        A b = (*a);
        a->set(32);
        cout<<"b: "<<b.print()<<endl;
        b.set(99);
        cout<<"a: "<<a->print()<<endl;
        delete a;
        system("pause");
        return 0;
    }
    =>a:5
      拷贝构造函数执行...
      b:5
      a:32

    注:此时,打开delete a;语句的注释不会奔溃,是由于两个对象指向的内存空间不是一样的了,因此可以独立释放内存空间,不会影响到另一个对象。


参考文献:
[1]《C++全方位学习》范磊——第十章
[2]《C++程序设计教程(第二版)》钱能——第五章、第六章、第七章
[3]《C++ Primer(第5版)》王刚 杨巨峰——第一章、第六章
[4] 百度搜索关键字:C++函数、构造函数、拷贝构造函数、浅层拷贝构造函数、深层拷贝构造函数

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值