C++ 类中默认6个函数及被调用时机

1. 6个函数代码及详细注释

在线代码链接跳转

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <span>

using namespace std;
using ID=unsigned int;

class Test {
 public:
  static ID genId;
  int mdata;

  //1. 默认构造函数: 当对象被创建而没有提供任何参数时调用。
  Test() : mid(++genId), mdata(0) {
    std::cout << "Default constructor called, mid = " << mid << std::endl;
  }

  //2. 析构函数: 当对象的生命周期结束时调用,例如对象离开其作用域或被 delete。
  virtual ~Test() {
    std::cout << "Destructor called, mid = " << mid << std::endl;
  }

  //3. 拷贝构造函数: 当一个对象以值的方式传递给函数或从函数返回,或者用一个同类型的其他对象初始化一个新对象时调用。
  Test(const Test& other) : mdata(other.mdata) {
    mid = ++genId;
    std::cout << "Copy constructor called, mid = " << mid << std::endl;
  }

  //4. 拷贝赋值运算符: 当一个对象被用来给另一个同类型的已存在对象赋值时调用。
  Test& operator=(const Test& other) {
    this->mdata = other.mdata;
    mid = ++genId;
    std::cout << "Copy assignment operator called, mid = " << mid << std::endl;
    return *this;
  }

  //5. 移动构造函数: 当一个对象被用来初始化另一个对象,且源对象可作为右值引用时调用。
  Test(Test&& other) : mid(++genId), mdata(other.mdata) {
    //other.mid = -1;    // 理论上即将消失的对象的数据应该被clear, 但是这里为了追踪obj 生命周期就没有修改
    other.mdata = -1;  // 理论上即将消失的对象(右值)的数据应该被clear
    std::cout << "Move constructor called, mid = " << mid << std::endl;
  }

  //6. 移动赋值运算符: 当一个对象被用来给另一个同类型的已存在对象赋值,且源对象可作为右值引用时调用
  Test& operator=(Test&& other) {
    if (this != &other) {
      mid = ++genId;
      //other.mid = -1;    // 理论上即将消失的对象的数据应该被clear, 但是这里为了追踪obj 生命周期就没有修改
      other.mdata = -1;   // 理论上即将消失的对象(右值)的数据应该被clear
      std::cout << "Move assignment operator called, mid = " << mid << std::endl;
    }
    return *this;
  }

 private:
  ID mid;
};

Test process_1(Test t) {
  cout<<"process_1 +"<<endl;
  Test tmp;
  cout<<"process_1 -"<<endl;
  return tmp;
}
Test process_2(Test& t) {
  cout<<"process_2 +"<<endl;
  Test tmp;
  cout<<"process_2 -"<<endl;
  return tmp;
}
Test process_3(Test&& t) {
  cout<<"process_3 +"<<endl;
  Test tmp;
  cout<<"process_3 -"<<endl;
  return tmp;
}
Test process_4(Test&& t) {
  cout<<"process_4 +"<<endl;
  Test tmp;
  cout<<"process_4 -"<<endl;
  return {tmp};
}

ID Test::genId = 0;

int main() {

  //1. 调用默认构造函数
  cout<<"== 1. 调用默认构造函数 =="<<endl;
  {
    Test p1;                    // 调用默认构造函数
    Test* p2 = new Test;
    Test* p3 = new Test();
    Test* p4(new Test);
    Test* p5{new Test()};
    shared_ptr<Test> p6{new Test, [](auto p) {
                        cout<<"deleter called +"<<endl;
                        delete p;
                        cout<<"deleter called -"<<endl;
                        }};
    shared_ptr<Test> p7 = make_shared<Test>();

    delete p2;                  // 调用析构函数
    delete p3;
    delete p4;
    delete p5;

    cout<<"<Leaving scope {} soon>"<<endl;
  }
  cout<<"== 1. 调用默认构造函数 =="<<endl<<endl;

  //3. 调用拷贝构造函数
  cout<<"== 3. 调用拷贝构造函数 =="<<endl;
  {
    Test e1;                         // 调用默认构造函数
    Test e2{e1};                     // 拷贝构造 [ Test e2(e1); Test e2{e1}; Test e2 = {e1};
    Test e3(e1);                     // 拷贝构造
    Test e4 = e1;                    // 拷贝构造
    Test& e7 = e1;                   // 特别注意:代码声明了一个指向 Test 类实例 e1 的引用 e3,并没有新的对象产生,所以不会调用任何构造函数
    shared_ptr<Test> p4(new Test()); // 调用Test默认构造函数,等价写法:p4(new Test()) or p4{new Test} or  p4{new Test()}
    shared_ptr<Test> p5 = p4;        // 等价写法: p5(p4) or p5{p4}; 此处都是调用shared_ptr的拷贝构造函数

    cout<< "$$ 数组测试 (1) $$"<<endl;
    Test e5[3] = {e1,e1,e1};         // 拷贝构造 分别调用三次
    Test e6[3] {e1,e1,e1};           // 拷贝构造 分别调用三次
    cout<< "$$ 数组测试 (2) $$"<<endl;
    vector<Test> vec1= {e1,e1};
    vector<Test> vec2 {e1,e1};
    cout<< "$$ 数组测试 (3) $$"<<endl;
    span<Test> s1(begin(vec1),end(vec2));
    cout<< "$$ 数组测试 (4) $$"<<endl;

    cout<<"## 函数相关测试  ##"<<endl;
    cout<<"begin process_1(值传递)"<<endl;
    Test ret1 = process_1(e1);
    cout<<"begin process_2(引用传递)"<<endl;
    Test ret2 = process_2(e1);
    cout<<"begin process_3(右值引用传递)"<<endl;
    Test ret3 = process_3(std::move(e1));
    cout<<"begin process_4(右值引用传递 + return {tmp};)"<<endl;
    Test ret4 = process_4(std::move(e1));     // 注意:函数内部 "return {tmp};" 返回会多执行一次拷贝构造函数
    cout<<"## 函数相关测试  ##"<<endl;
    
    cout<<"<Leaving scope {} soon>"<<endl;
  }
  cout<<"== 3. 调用拷贝构造函数 =="<<endl<<endl;


  cout<<"== 4. 拷贝赋值运算符 =="<<endl;
  {
    Test a;
    Test b;
    b = a;                     // 调用拷贝赋值运算符

    cout<<"<Leaving scope {} soon>"<<endl;
  }
  cout<<"== 4. 拷贝赋值运算符 =="<<endl<<endl;


  cout<<"== 5. 移动构造函数 =="<<endl;
  {
    Test a;
    Test b = std::move(a);     // 调用移动构造函数
    
    cout<<"<Leaving scope {} soon>"<<endl;
  }
  cout<<"== 5. 移动构造函数 =="<<endl<<endl;
  
  cout<<"== 6. 移动赋值运算符 =="<<endl;
  {
    Test a;
    Test b;
    b = std::move(a);         // 调用移动赋值运算符 operator=(Test&&)
    
    cout<<"<Leaving scope {} soon>"<<endl;
  }
  cout<<"== 6. 移动赋值运算符 =="<<endl<<endl;
  return 0;
}

2. 代码的执行结果

== 1. 调用默认构造函数 ==
Default constructor called, mid = 1
Default constructor called, mid = 2
Default constructor called, mid = 3
Default constructor called, mid = 4
Default constructor called, mid = 5
Default constructor called, mid = 6
Default constructor called, mid = 7
Destructor called, mid = 2
Destructor called, mid = 3
Destructor called, mid = 4
Destructor called, mid = 5
<Leaving scope {} soon>
Destructor called, mid = 7
deleter called +
Destructor called, mid = 6
deleter called -
Destructor called, mid = 1
== 1. 调用默认构造函数 ==

== 3. 调用拷贝构造函数 ==
Default constructor called, mid = 8
Copy constructor called, mid = 9
Copy constructor called, mid = 10
Copy constructor called, mid = 11
Default constructor called, mid = 12
$$ 数组测试 (1) $$
Copy constructor called, mid = 13
Copy constructor called, mid = 14
Copy constructor called, mid = 15
Copy constructor called, mid = 16
Copy constructor called, mid = 17
Copy constructor called, mid = 18
$$ 数组测试 (2) $$
Copy constructor called, mid = 19
Copy constructor called, mid = 20
Copy constructor called, mid = 21
Copy constructor called, mid = 22
Destructor called, mid = 20
Destructor called, mid = 19
Copy constructor called, mid = 23
Copy constructor called, mid = 24
Copy constructor called, mid = 25
Copy constructor called, mid = 26
Destructor called, mid = 24
Destructor called, mid = 23
$$ 数组测试 (3) $$
$$ 数组测试 (4) $$
## 函数相关测试  ##
begin process_1(值传递)
Copy constructor called, mid = 27
process_1 +
Default constructor called, mid = 28
process_1 -
Destructor called, mid = 27
begin process_2(引用传递)
process_2 +
Default constructor called, mid = 29
process_2 -
begin process_3(右值引用传递)
process_3 +
Default constructor called, mid = 30
process_3 -
begin process_4(右值引用传递 + return {tmp};)
process_4 +
Default constructor called, mid = 31
process_4 -
Copy constructor called, mid = 32
Destructor called, mid = 31
## 函数相关测试  ##
<Leaving scope {} soon>
Destructor called, mid = 32
Destructor called, mid = 30
Destructor called, mid = 29
Destructor called, mid = 28
Destructor called, mid = 25
Destructor called, mid = 26
Destructor called, mid = 21
Destructor called, mid = 22
Destructor called, mid = 18
Destructor called, mid = 17
Destructor called, mid = 16
Destructor called, mid = 15
Destructor called, mid = 14
Destructor called, mid = 13
Destructor called, mid = 12
Destructor called, mid = 11
Destructor called, mid = 10
Destructor called, mid = 9
Destructor called, mid = 8
== 3. 调用拷贝构造函数 ==

== 4. 拷贝赋值运算符 ==
Default constructor called, mid = 33
Default constructor called, mid = 34
Copy assignment operator called, mid = 35
<Leaving scope {} soon>
Destructor called, mid = 35
Destructor called, mid = 33
== 4. 拷贝赋值运算符 ==

== 5. 移动构造函数 ==
Default constructor called, mid = 36
Move constructor called, mid = 37
<Leaving scope {} soon>
Destructor called, mid = 37
Destructor called, mid = 36
== 5. 移动构造函数 ==

== 6. 移动赋值运算符 ==
Default constructor called, mid = 38
Default constructor called, mid = 39
Move assignment operator called, mid = 40
<Leaving scope {} soon>
Destructor called, mid = 40
Destructor called, mid = 38
== 6. 移动赋值运算符 ==

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值