C++ 特殊成员函数:默认构造函数、默认析构函数、复制构造函数、赋值运算符

1、绪言

  特殊成员函数:程序员没有进行定义,但C++会自动提供的成员函数
特殊成员函数如下:
  ·默认构造函数
  ·默认析构函数
  ·复制构造函数
  ·赋值运算符
  ·地址运算符
  ·移动构造函数
  ·移动赋值运算符

  如果程序使用对象的方式需要,编译器会自动生成复制构造函数、赋值运算符、地址运算符三个函数的定义。例如,需要将一个对象赋给另一个对象,编译器将提供赋值运算符的定义。
移动构造函数和移动赋值运算符是C++11中提供的两个特殊成员函数。这两个函数会有单独的文章讲解,此处不做解读。

2、默认构造函数

  如果没有提供任何构造函数,C++将创建默认构造函数。默认构造函数不接受任何一个参数,也不执行任何操作。 以Klunk类为例,Klunk类的默认构造函数如下:

Klunk::Klunk(){ }//implicit default constructor

**如果定义了构造函数,C++将不会定义默认构造函数。如果希望在创建对象时不显式的对它进行初始化,则必须显式地定义默认构造函数。

2.1默认构造函数形式一

  默认构造函数存在两种形式,一种与编译器自动生成的类似,没有任何参数,可以使用它来设置特定的值。

Klunk::Klunk()//explicit default constructor
{
   klunk_ct = 0;
...
 }

2.2默认构造函数形式二

  还有一种格式是 带参数的构造函数,只要所有参数都有默认值。 Klunk类可以包含如下内联构造函数:

Klunk(int n = 0) {klunk_ct = n;}

需要注意的是 默认构造函数只能有一个,也就是说上述两种形式的默认构造函数只能在程序中出现一个。 如果Klunk中存在如下定义,则会产生二义性。

Klunk::Klunk(){klunk_ct = 0;}//constructor#1
Klunk(int n = 0) {klunk_ct = n;}//ambiguous constarutor#2

  当使用 Klunk bus; 语句来创建对象时,既与构造函数#1匹配,也与构造函数#2(使用默认参数0)匹配。这将导致编译器发出一条错误消息。

3、 默认析构函数

  在未定义任何析构函数时,编译器会自动生成默认析构函数,自动创建的默认析构函数与默认构造函数相似,不接受任何参数,也不执行任何操作。以Klunk类为例,默认析构函数如下:

Klunk::~Klunk(){}

4、复制构造函数

  复制构造函数用于将一个对象复制到新创建的对象中。用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中。 类的复制构造函数原型通常如下:

Class_name(const Class_name &);

它接受一个指向类对象的常量引用作参数。例如,Klunk类的复制构造函数原型如下:

Klunk(const Klunk &);

对于复制构造函数必须要知道的两点:何时调用和有何功能。

4.1、何时调用复制构造函数

1.最常见的情况是将新对象显式地初始化为现有的对象。假设klunk是一个Klunk对象,以下4种声明都将调用复制构造函数:

Klunk ditto(metto);
Klunk metoo = metto; 
Klunk also = Klunk(metto);
Klunk *pKlunk = new Klunk(metto);

  其中中间两种可能会使用复制构造函数直接创建metoo何also,也可能使用复制构造函数生成一个临时对象,然后将临时对象的内容赋给metoo和also,这取决于具体的实现。最后一种声明使用metto初始化一个匿名对象,并将新对象的地址赋给pKlunk指针。

2.每当程序生成了对象副本时,编译器都将使用复制构造函数。具体地说,当函数按值传递对象函数返回对象时,都将使用复制构造函数。记住,按值传递意味着创建原始变量的一个副本。

void function(Class_name);//函数定义声明,参数为值传递方式
function(object_name);

3.编译器在生成临时对象时,也将使用复制构造函数。例如两个对象相加时,编译器会生成一个临时的类对象来保存中间结果。

  由于按值传递对象将调用复制构造函数,因此应该按引用传递对象。这样可以节省调用构造函数的时间以及存储新对象的空间

4.2、复制构造函数有什么功能

  默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员函数。静态成员不受影响,因为它们属于整个类,而不是各个对象。

5、赋值运算符

  赋值运算符接受并返回一个指向类对象的引用。其函数原型如下:

Class_name & Class_name::operator=(const Class_name &);

将已有的对象赋给另一个对象时,将使用重载的赋值运算符

Class_name objectaB(m,n,"string");
...//对两个
Class_name  objectaA;
objectaA = objectB;//此处将调用赋值运算符

  赋值运算符与复制构造函数相似,赋值运算符的隐式实现也是对成员进行逐个复制。如果成员本身就是类对象,则程序将使用为这个类定义的赋值运算符来复制成员,但静态数据成员不受影响。
如果类中存在new动态开辟内存的情况,编译器自动提供的赋值运算符就不够用了,自动生成的赋值运算符和复制构造函数只是对指针对象进行浅拷贝,调用对象的指针成员指向被调用对象的指针成员指向的内存,当对两个对象进行清理工作时,会出现错误,对同一片内存空间进行重复清理。所以当类中存在动态内存分配时,需要自定义复制构造函数和赋值运算符,进行深拷贝,这样就可以解决对已清除的空间再进行清理的问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值