C++学习笔记总结练习:构造函数和析构函数

59 篇文章 0 订阅
35 篇文章 0 订阅

拷贝控制

参考文献

类的特殊函数

  • 初始化——构造函数
  • 拷贝——拷贝构造函数
  • 移动——移动构造函数
  • 赋值——拷贝赋值运算符、移动赋值运算符
  • 销毁——析构函数
默认构造(无参)   T()

拷贝构造       T(const T& )
移动构造       T(T&&)

拷贝赋值       T& operator=(T& )
移动赋值       T& operator=(T&& )

析构         ~T()

1 构造函数

与类同名的,没有返回值的函数,用来创建、拷贝、移动、销毁该类的对象。

1.1 合成构造函数

编译器自动生成的一系列构造函数。包括以下几种

  • 合成默认构造函数
    • 当用户定义了任意类型的构造函数,编译器不再自动生成合成默认构造函数
  • 合成拷贝构造函数
    • 即是用户定义了其他类型的构造函数,编译器还会自动生成合成拷贝构造函数。
    • 编译器自动生成的拷贝构造函数。从给定的对象中依次将每个非static成员拷贝到正在创建的对象当中。
  • 合成析构函数
    • 系统自动生成的析构函数。

1.2 默认构造函数和普通构造函数

  • 默认构造函数是无参构造函数
  • 普通构造函数是一系列有参数的构造函数。

1.3 拷贝构造函数和拷贝赋值运算符

唯一参数是当前类类型,或者当前类型的const引用。

示例

class Foo{
    Foo();
    Foo(const Foo&)//拷贝构造函数
}

赋值初始化(拷贝构造函数)

赋值初始化的时候会自动调用拷贝构造函数。

string nies = string("efji");
  • 当我门使用 赋值= 运算符时,发生赋值初始化,执行拷贝构造函数。
  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为费引用类型的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员

赋值运算符(拷贝赋值运算符)

普通赋值的时候,会调用重载的赋值运算符。

  • 编译器会自动生成合成拷贝赋值运算符
  • 需要重载赋值运算符。

1.4 移动构造函数和移动赋值运算符

  • 在面向对象中,有的类是可以拷贝的,例如车、房等他们的属性是可以复制的,可以调用拷贝构造函数,有点类的对象则是独一无二的,或者类的资源是独一无二的,比如 IO 、 std::unique_ptr等,他们不可以复制,但是可以把资源交出所有权给新的对象,称为可以移动的。
  • C++11最重要的一个改进之一就是引入了move语义,这样在一些对象的构造时可以获取到已有的资源(如内存)而不需要通过拷贝,申请新的内存,这样移动而非拷贝将会大幅度提升性能。例如有些右值即将消亡析构,这个时候我们用移动构造函数可以接管他们的资源。
#include <iostream>
#include <cstring>

using namespace  std;

class A{
public:
    //默认构造函数
    A():i(new int[500]){
      cout<<"class A construct!"<<endl;
   }
   //拷贝构造函数
    A(const A &a):i(new int[500]){
      memcpy(i, a.i,500*sizeof(int));
      cout<<"class A copy!"<<endl;
   }
   //拷贝赋值运算符
    A &operator =(A &rhs) noexcept{
      // check self assignment
      if(this != &rhs){
         delete []i;
         i = rhs.i;
      }
      cout<< "class A copy and assignment"<<std::endl;
      return *this;
   }
   //移动构造函数
    A(A &&a)noexcept:i(a.i)
   {
      a.i = nullptr;
      cout<< "class A move"<<endl;
   }
   //移动赋值运算符
    A &operator =(A &&rhs) noexcept{
      // check self assignment
      if(this != &rhs){
         delete []i;
         i = rhs.i;
         rhs.i = nullptr;
      }
      cout<< "class A move and assignment"<<std::endl;
      return *this;
   }
   //析构函数
   ~A(){
      delete []i;
      cout<<"class A destruct!"<<endl;
   }

private:
   int *i;
};

A get_A_value(){
    return A();
}
void pass_A_by_value(A a){

}
int main(){
    A a = get_A_value();
    return 0;
}
  • 在移动构造函数中,我们做了什么呢,我们只是获取了被移动对象的资源(这里是内存)的所有权,同时把被移动对象的成员指针置为空(以避免移动过来的内存被析构),这个过程中没有新内存的申请和分配,在大量对象的系统中,移动构造相对与拷贝构造可以显著提高性能!这里noexcept告诉编译器这里不会抛出异常,从而让编译器省一些操作(这个也是保证了STL容器在重新分配内存的时候(知道是noexpect)而使用移动构造而不是拷贝构造函数),通常移动构造都不会抛出异常的。

注意事项:

  • 偷梁换柱直接“浅拷贝”右值引用的对象的成员;
  • 需要把原先右值引用的指针成员置为 nullptr,以避免右值在析构的时候把我们浅拷贝的资源给释放了;
  • 移动构造函数需要先检查一下是否是自赋值,然后才能先delet自己的成员内存再浅拷贝右值的成员,始终记住第2条。

1.5 委托构造函数

  • 使用已有的构造函数初始化。

2 析构函数

定义析构函数

  • 析构函数与构造函数对应,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。
  • 类的成员函数,由拨浪号接类名构成,没有返回值,不接受参数。不能被重载,一个类只有一个析构函数。
class Foo{
    public:
        ~Foo();
}

原理

  • 在一个析构函数中,首先执行函数体,然后销毁成员。成员按初始化顺序逆序销毁。
  • 智能指针成员在西沟阶段会自动销毁。

何时调用

  • 变量离开作用域被销毁
  • 一个对象被销毁
  • 容器被销毁
  • 动态对象,使用delete

对象析构顺序

  1. 派生类本身的析构函数;
  2. 对象成员析构函数;
  3. 基类析构函数。

3 虚函数与构造函数和析构函数

构造函数不必是虚函数

  1. 对象通过虚函数指针访问虚函数。在执行构造函数之前,虚函数指针没有创建,所以即使声明为虚函数,也不会有多态,所以不必要是虚函数。

析构函数必须是虚函数

  1. 删除动态运行时的具体对象。
  2. 普通对象如果不被继承,析构函数可以不使用虚函数。避免生成虚函数表和虚函数指针,浪费内存空间。

4 三五法则

在较新的 C++11 标准中,为了支持移动语义,又增加了移动构造函数和移动赋值运算符,这样共有五个特殊的成员函数,所以又称为“C++五法则”;也就是说,“三法则”是针对较旧的 C++89 标准说的,“五法则”是针对较新的 C++11 标准说的;为了统一称呼,后来人们干把它叫做“C++ 三/五法则”;

  1. 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数。
  2. 需要拷贝操作的类也需要赋值操作,反之亦然。
  3. 析构函数不能是删除的
  4. 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的。
  5. 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作。

5 概念区分:声明declare、定义define、初始化initialize、赋值assign

声明declare

  • 声明一个符号。如果有extern,则表示变量在外部顶底,链接其他问件事,匹配外部定义的变量。

定义define

  • 分配内存、指定变量名。

初始化initialize

  • 对象创建时获得的初始值。初始化的含义是,在创建变量的时候赋予其一个初始值。

赋值assign

  • 赋值的含义是,将当前的值擦除,而以一个新的值来代替。
  • 不能是简单的覆盖,将当前值擦除,需要调用当前对象的析构函数,对指针变量进行析构,如果只是简单的覆盖,肯定不行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mDeepLearning4205

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值