C++随笔-复制构造函数与赋值运算符

复制构造函数

坑得一逼...

当使用实例化了的自定义类对象给一个新声明的自定义类对象赋值时,如果没有定义参数为自定义类对象的构造函数,则编译器会自动生成一个复制构造函数...

注意:复制构造函数只在新创建的对象进行初始化时被调用,初始化(赋值)的对象类型同为用户自定义类型。

例如下列代码:

Classname instance1;
Classname instance2 = instance1;

实际上等价于:

Classname instance2 = Classname(instance1);

所以,如果在自定义类的定义中,没有接受参数为自定义类对象的构造函数

class Classname{
private:
    ...
public:
    Classname();
    ~Classname();
    ...
}

则编译器会自动生成一个接受自定义类对象为参数的复制构造函数。

所以如果是依靠编译器自动生成的构造函数,在开发过程中可能会遇到意想不到的问题。所以建议主动定义接受自定义类对象的复制构造函数

class Classname{
private:
    ...
public:
    Classname();
    Classname(const Classname &);
    ~Classname();
    ...
}

 

赋值运算符函数

将一个已有的对象赋予另一个对象时,将调用重载的赋值运算符函数。

当自定义类没有重载复制运算符函数时,编译器会自动生成一个默认赋值运算符函数。默认赋值运算符函数也是和复制构造函数一样对类成员逐一复制。因此在没有主动定义复制构造函数而使用自动生成的默认复制构造函数时所遇到的问题,在默认赋值运算符函数上同样会遇到。解决思路也相近。

重要的是,注意复制构造函数与赋值运算符函数的辨析。

  • 复制构造函数:既然是构造函数,那必然是用于自定义类对象创建的时候。
  • 赋值运算符函数:赋值运算符函数更强调在对已完成声明的自定义类对象使用另一个已存在的自定义类对象进行赋值的时候进行调用。

在初始化过程中,有可能会分为两个步骤:调用复制构造函数创建一个临时变量,然后通过赋值将临时对象的值复制到新对象中。也就是说,可能经历了调用复制构造函数和调用赋值运算符函数两个过程才完成的类对象的初始化。但无论如何,初始化过程一定会调用复制构造函数。如果想要避免上述两个步骤的初始化过程中存在的临时类对象的创建和释放(对应类对象构造函数和析构函数的调用),可以重载赋值运算符函数,使其能够唯一接受一个参数,参数类型为用于赋值的对象的类型,从而避免了临时对象的创建和释放。

总结

所谓复制构造函数,就是一个用于复制类实例对象的构造函数,其特殊在某些场景下会被自动调用。

复制构造函数的调用场景

  1. 新建一个对象并将其初始化为一个现有的同类对象时,复制构造函数将被调用
  2. 程序中任何生成对象副本时,都会调用复制构造函数。例如按值传递时创建了一个对象副本、返回对象时创建对象副本等等。由于按值传递等情况会生成对象副本,所以应当尽可能使用按引用传递对象的方式,这样可以避免调用构造函数,从而节省内存占用和调用构造函数时所花费的时间。

注意,初始化时的赋值和常规的赋值不一样。在初始化时赋值会调用复制构造函数,而常规赋值时并不会。

在初始化过程中,有可能会分为两个步骤:调用复制构造函数创建一个临时变量,然后通过赋值将临时对象的值复制到新对象中。也就是说,可能经历了调用复制构造函数和调用赋值运算符函数两个过程才完成的类对象的初始化。但无论如何,初始化过程一定会调用复制构造函数。

如果类对象成员中有存放new分配的内存的指针,则需要定义一个复制构造函数。否则可能会遇到析构函数中使用delete去释放已经被释放过的内存。因为指针中存放的是地址,如果调用默认的复制构造函数,则是对成员逐一进行复制的,所以一个new分配的内存地址会被复制,存在于两个对象中,一个对象为目标生成的对象,另一个对象为生成过程中的对象副本。但是无论是否是副本,在对象过期时都会调用析构函数,所以如果一个内存地址存在于两个对象中,并且在对象的析构函数中都会对该内存进行释放,那么就会遇到一个new分配的内存被delete释放两次,这会导致编译器报错,甚者会导致其他实现上的问题。所以解决方法就是,使用deep copy深拷贝,将内存地址对应内存存放的内容一起拷贝,从而使得复制构造函数调用时,每个成员指针所指向的动态内存都是独立的(每个类中分配的动态内存都只被一个对象的成员指针所指)。

 

特殊成员函数

自定义类中是存在一些特殊的成员函数的,这些成员函数在某些条件下是会自动定义的

  • 如果没有定义构造函数,则会自动定义默认构造函数
  • 如果没有定义析构函数,则会自动定义默认析构函数
  • 如果没有定义复制构造函数,则会自动定义复制构造函数
  • 如果没有定义赋值运算符函数,则会自动定义赋值运算符函数
  • 如果没有定义地址运算符函数,则会自动定义地址运算符函数
  • 移动构造函数
  • 移动赋值运算符
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值