C++基础语法:复制构造函数,赋值构造函数及浅复制,深复制

前言

       "打牢基础,万事不愁" .C++的基础语法的学习

引入

        前一篇帖子C++基础语法:类构造函数之普通构造函数-CSDN博客讲了普通构造函数,还有 复制构造函数,赋值运算符以及延伸出来的浅复制和深复制问题,做个补充.

        内容主要参考自<<C++ Prime Plus>> 6th Edition 第12章 类和动态内存分配

题外话

        先说说个人心得,这部分内容是C++学习首次接触时,开始觉得杂乱的地方,也可以说是第一个难点(当然后面还有多种继承方式,多重继承啊,模板啊,算法啊,也不容易).所以需要好好梳理他的来龙去脉. 类可以包络一切数据和逻辑,类写完了,程序差不多就完成了.所以构造类对象比较重要.

        再谈谈学习方法,纯记忆学习,很牢靠也很让人踏实.但有两个问题,一是记不记得了那么多,有的人记忆力没那么好.二是容易造成一个不爱思考的"舒适区",编程本身就是在表达逻辑,比使用逻辑要深一层.所以学习最好能建立逻辑链,以"现有知识"推导"未知",同时把"未知"变为"已知"

复制构造函数

       对象的生成方法,不止使用普通构造函数,还可以使用复制构造函数.他们在形式上有差别.普通构造函数,使用了非对象做参数;复制构造函数,使用已生成的对象做参数,生成新的对象.例如: 

#include<iostream>
using namespace std;
class Person {                                  //类声明
    string description;
    double money;
public:
    Person(const string& des, double mo) :description(des), money(mo) {}
    double getMoney() {
        return money;
    }
    string getDescription() {
        return description;
    }
};

int main(void) {
    Person Annie = Person("beautiful", 5000);   //普通构造函数生成对象安妮
    Person Julie(Annie);                        //复制构造函数生成对象朱丽,属性值和安妮相同
    cout << "安妮的私房钱:" << Annie.getMoney() << endl;
    cout << "朱莉的私房钱:" << Julie.getMoney() << endl;
}

----对象Annie的生成调用了普通构造函数,对象Julie的生成调用了复制构造函数.

复制构造函数还有另外几种形式:   不用可以去记忆,他们的共同特征是以已有对象做参数.

Person Julie=Annie;
Person Julie=Person(Annie);
Person *pJulie=new Person(Annie);

赋值运算符

        和复制构造函数几乎相同:         

    Person Rebecca;                    //声明对象瑞贝卡
    Rebecca = Julie;                   //将Julie的属性值赋给Rebecca
    cout << "瑞贝卡的私房钱:" << Rebecca.getMoney() << endl;
=============================================================
    Person(){}                        //类声明中加上默认构造函数,否则报错

 函数原型及浅复制

        每个类存在默认复制构造函数和默认赋值运算符,本例中的原型是:

Person (const Person&);                    //默认复制构造函数原型
Person& Person::operator=(const Person&);  //赋值运算符重载

       他表达的逻辑: 传入已有对象时,将对象的属性值赋给新对象,

===========================内容分割线:小思考=============================

        疑问:原型内部逻辑怎样实现属性值复制?

        java里有反射机制,可以获得每个类的属性和方法,C++没有明确说明是怎样实现"反射"的.但可以肯定的是:一定存在类似反射的机制,可以获得每个类的属性.他有可能被封装进源码里,没有将接口开放给程序员.       

===========================内容分割线:小思考=============================

 浅复制的问题

       篇幅所限,代码参考<<C++ Prime Plus>> 6th Edition 第12章 类和动态内存分配 中的String类

        1>无法处理类静态变量

        这里必须说明,因为静态变量num_strings是表示生成对象的数量,在普通构造函数中有所表示---每生成一个对象,num_strings++;而在默认的复制构造函数和赋值符"="的定义中,没有表示,所以会造成调用后两种方法生成对象时,不会记录到静态变量中.----这不是语法造成的,而是逻辑上的错误.自定义默认构造函数和赋值符解决

        2>当传入指针,并在析构函数中调用delete删除指针指向对象,又把对象浅复制,并且两次调用析构函数.

         第2个问题的条件比较长,分两步来理解.

        首先是类设计时,有指针传入; 在析构函数中调用delete删除指针指向的对象.

        然后在生成对象object后,浅复制生成了新对象newObject(可能有多个).两次或多次调用析构函数释放内存,使删除出错.   -----原因:C++两次释放同一块空间会出现错误.

浅复制问题的解决:深复制

         深复制可以解决浅复制带来的问题,操作也比较简单,不管是普通构造函数,复制构造函数,赋值运算符重载,都用new开辟一块内存空间,复制传入指针指向的数据,在析构函数里定义delete.那么不管怎样复制对象,每生成一个对象,调用delete一次,不会出错.

深复制的问题

        以下内容书上没有,属于自己总结,有版权:)

===========================内容分割线:小思考============================         

        为什么要给类传入指针?

        C++是追求性能的语言,试想这种场景:尽可能的占用少的内存.内存分为三块:静态内存,局部变量使用内存,动态内存;那么可以推导出程序运行占用的内存只有类定义,函数定义(这些空间无法节省),所有的数据都用new动态生成,用完即删除.所以传入的指针都是new生成的.

===========================内容分割线:小思考============================ 

        深复制有以下两个问题:

        1>和以上内容存在悖论,为了正确的删除,占用了更多的内存空间.

        2>给指针分配多少内存合适?书上的例子是char *指针,可以求出其占用空间,若指针指向数组,可以用sizeof()求出,如果指针指向链表或者其他数据集合又该怎么办呢?

深复制问题的解决

        1>所有构造函数定义和赋值运算符重载都不用new分配空间(比书上还省一点),什么时候删除指针指向的数据,全由程序员自己掌握.举例:

class Demo {                                    //演示类,属性有指针
    int* demo;
public:
    Demo(int * de):demo(de){}
    void show() {
        cout << "数组中的第1个数字是:" << *demo << endl;
    }
// ~Demo() { delete[] demo; };               //若启用则删除传入指针指向数据,和"delete[] a"互斥
};
================================================================
int main(void) {
    int *a=new int[] { 10,20 };                 //动态分配空间建立数组,生成指针a
    Demo *d=new Demo(a);                        //传入指针,并动态生成对象d
    d->show();
    delete d;                                   //删除动态生成对象d
    cout << "现在数组中第1个数字是" << *a << endl;
    delete[] a;                                 //手动删除a,需要注释~Demo()否则报错
}

----说明:又回到浅复制了, 代码少了许多(如果解决静态变量问题需补上定义)

        2>不要传入指针,把数据放到一个类里,传入该类对象的引用.用对象引用去控制数据,内存占一点也没关系,省事多了.需要新对象的话再用普通构造函数生成一个

----说明:不要说深复制,浅复制都被省了,整个12章的内容几乎都被省去了.对作者和书的内容无可厚非,设计了这种机制,也讲清楚了.

 小结

        C++构造函数的理解和应用 

更新

        C++语法应用:深复制的一种替代方案-CSDN博客这篇帖子有对深复制的一种写法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

jllws1

你的鼓励是我创作的动力,谢谢

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

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

打赏作者

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

抵扣说明:

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

余额充值