C++的默认成员以及函数详解(下篇)

拷贝构造函数

来接着来介绍这位默认成员函数
在创建对象时,难免不去创建一个一样的对象,这个时候就需要拷贝构造函数的帮助了
还是接着用Date类来举例子

Date d1(2023, 2, 19);
Date d2(d1); // d2的值跟d1一样

上面d2的就用了拷贝构造来复制d1的值

拷贝构造函数:
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存
在的类类型对象创建新对象时由编译器自动调用。

特性

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

拷贝构造函数的用法:

#include <iostream>
 
class Date {
public:
    Date(int year = 1970, int month = 1, int day = 1) {
        _year = year;
        _month = month;
        _day = day;
    }
 
    /* Date(Date d); 会无穷递归下去*/
    Date(Date& d) { 
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
private:
    int _year;
    int _month;
    int _day;
};
 
int main(void)
{
    Date d1(2022, 3, 9);
    Date d2(d1); 
    
    return 0;
}

当自定义函数产生拷贝时,必需要调用拷贝构造函数,但是要先需要传参数,然后传值传参又需要调用拷贝构造,周而复始无限循环,直接无限月读(doge)
在这里插入图片描述
传参时,用了const修饰,是为了防止下面的情况:

/* Date d2(d1); */
Date(Date& d) 
{
    d._year = _year;
    d._month = _month;
    d._day = _day;
}

这个时候,编译器不会报错,但是结果会变为随机值,加了const的话就不会出现上面的情况了,因为当初出现了上面的这种情况,编译器就不会放你编译通过

默认生成的拷贝构造

  1. 对于内置类型的成员,会完成按字节序的拷贝(把每个字节依次拷贝过去)。
  2. 对于自定义类型成员,会再调用它的拷贝构造。

深拷贝和浅拷贝了解下:
内置类型拷贝就是浅拷贝(把每个字节依次拷贝过去)
深拷贝源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

编译器自动生成的默认拷贝构造函数,对于内置类型和自定义类型都会拷贝处理。但是处理的细节是不一样的,这其中的细节要深究

#include<iostream>
using namespace std;
 
class Date {
    public:
        Date(int year = 0, int month = 1, int day = 1) {
            _year = year;
            _month = month;
            _day = day;
        }
        
        // 注释掉拷贝构造,让编译器自动生成
        // Date(Date& d) {
        //     _year = d._year;
        //     _month = d._month;
        //     _day = d._day;
        // }
 
        void Print() {
            printf("%d-%d-%d\n", _year, _month, _day);
        } 
    
    private:
        int _year;
        int _month;
        int _day;
};
 
int main(void)
{
    Date d1(1970, 1, 1);
 
    // 调用默认拷贝构造
    Date d2(d1);
    
    d1.Print();
    d2.Print();
    return 0;
}

结果:
在这里插入图片描述

像上述中的日期类中的成员变量无需去认真销毁,那是不是拷贝构造就没必要自己写了呢?
答案是当然不行的, 比如关于数组栈类中的会向堆上申请空间,而编译器只会傻瓜式的进行浅拷贝, 实列化一个 stack s1, 再去stack s2(s1), 此时若是只用默认生成的拷贝构造, 就会发生s1中的数组指针和s2中的数组指针指向同一块空间.
可想而知, s1 和 s2 进行的增删改都会互相影响, 而且再后绪去堆上释放s1,s2的指向的空间,此时同一块空间释放两次.当多次使用 free () 且内存地址作为输入时,会发生双重释放错误。 在同一个变量上调用 free () 两次可能会导致内存泄漏

  • 总结:对于常见的类,比如日期类,默认生成的拷贝构造能用。但是对于栈这样需要动态申请空间的类,则需要我们自己去针对性的编写拷贝构造。

析构函数

既然有初始化的构造函数,就有销毁、清理数据的析构函数.
构造函数也是默认成员函数,不写编译器也会自己生成一份,写了编译器就不会再自动生成了。

  • 对于内置类型成员变量不处理,而对于自定义类型的成员变量会调用它的默认构造函数。

析构函数完成对象中成员空间的清理。如果类对象中的空间是向堆申请的空间需要进行资源清理,才需要自己实现析构函数。

析构函数在对象生命周期到了以后自动调用,什么时候生命周期到了?如果是局部变量,出了作用域。全局和静态变量,整个程序结束。

  • 注意: 匿名对象的生命周期的就在当前行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值