类和对象之拷贝构造

既然我们可以通过传确定的参数来完成初始化,那么相同的两个类之间能不能通过拷贝的方式来完成初始化

class Date
{
public:
     Date(int year = 1, int month = 1, int day = 1)
     {
         _year = year;
         _month = month;
         _day = day;
     }

private:
     int _year;
     int _month;
     int _day;
};

int main()
{
     Date d1(2024,1,26);
     Date d2(d1);
     return 0;
}

以上代码中d1拷贝给了d2,并完成了初始化,里面成员变量的值和d1完全一样,这说明我们是可以传相同的类来初始化,但其实在d2完成初始化之前编译器帮你完成了一件事:拷贝构造。这里并没有显示的写拷贝构造函数,是因为编译器自动生成了一个默认拷贝构造,这种只发生在内置类型上的拷贝称为浅拷贝(也叫值拷贝)。

如果要将这里的拷贝构造函数补充完整应该是怎样的呢?

class Date
{
public:
     Date(int year = 1, int month = 1, int day = 1)
     {
         _year = year;
         _month = month;
         _day = day;
     }
     Date(Date d)//这样吗?
     {
         _year = d.year;
         _month = d.month;
         _day = d.day;
     }

private:
     int _year;
     int _month;
     int _day;
};

int main()
{
     Date d1(2024,1,26);
     Date d2(d1);
     return 0;
}

假如我们用一个自定义类型来接收参数,类型确实是匹配了,但遗憾的是,代码编译到这就不会往下走了,因为在你调用这个函数之前必须传参,而传参数必定又会调用新的拷贝构造....如此循环,就会发生无穷递归。

这时候就只能借助引用&,这样形参就是实参的别名,所以就不会引发新的拷贝构造。

此外,由于我们只是想把d2的值拿d1的值来初始化,也就是说d2的值可以改变而d1的值我们不希望它改变,故在接收时也可以用const修饰。

Date(const Date& d)

可能就会有人说了,既然编译器会帮我们做我们干嘛还要自己写拷贝构造函数

是的,这样的情况下我们的确不需要写拷贝构造,编译器自动生成的拷贝构造也能满足我们的需求,因为这里面只有int型。但我们要是想初始化一个顺序表呢,浅拷贝还能否完成

class my_vector
{
    int *_a;
    int _capacity;
    int _size;
 
public:
    my_vector(int n=4)
    {
	    _a = (int*)malloc(sizeof(int)*n);
	    if (_a == NULL)
	    {
	    	perror("malloc failed");
	    	return;
	    }
 
	    _size = 0;
	    _capacity = n;
    }
     ~my_vector()
     {
        free(_a);
        _a=nullptr;
        _size=_capacity=0;
     }
};

int main()
{
     my_vector v1(4);
     my_vector v2(v1);     
     return 0;
}

 可以看到,v2的确完成了拷贝,每个成员变量都和v1完全相同。看似完成了拷贝,而且非常合理,可是编译后却报异常,到底是哪里出现了问题

这里有个细节很容易被忽略:_a指向的空间地址也是完全相同。可是,不就是指向同一个地址嘛,怎么就出现异常呢

别忘了,这是动态申请出来的空间,到最后是要释放的,而这里肯定是要调用析构函数来释放。当v1的生命周期结束时,_a将被释放,v2的生命周期结束时,_a又会被释放一次,同一块空间被释放两次必然会引发异常。

那有没有什么办法让_a指向不同的空间地址,又让成员变量的值相同?

有,深拷贝。想要这样做靠编译器生成的默认拷贝构造肯定不行,那就只能自己写拷贝构造,让它完成深拷贝。

    my_vector(const my_vector& v)
    {
	    _a = (int*)malloc(sizeof(int)*v._capacity);
	    if (_a == NULL)
	    {
	    	perror("malloc failed");
	    	return;
	    }
        memcpy(_a, v._a, sizeof(int) *v._capacity;
	    _size = v._size;
	    _capacity = v._capacity;
    }

如此,便完成了深拷贝。

什么时候用深拷贝什么时候浅拷贝?

这个根据情况而定。有时我们只有值拷贝,不涉及到动态空间申请时,用浅拷贝就够了,毕竟编译器能够自动帮我们生成,但凡涉及到动态内存申请,我们应该使用深拷贝,否则就会出现如上错误

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值