1ms Faster
Home
  • Program

    • Lean in C++
  • Perfromance Engineering

    • [>>>>>]
  • Reading Note

    • [>>>>>]
  • ComputeArch

    • [_]
  • Compiler

    • [_]
  • System

    • [_]
Authoring
  • Categories
  • Tags
GitHub (opens new window)

Quincy Jet

We are.
Home
  • Program

    • Lean in C++
  • Perfromance Engineering

    • [>>>>>]
  • Reading Note

    • [>>>>>]
  • ComputeArch

    • [_]
  • Compiler

    • [_]
  • System

    • [_]
Authoring
  • Categories
  • Tags
GitHub (opens new window)
  • Lean in c++

  • Performance Engineering

  • Misc

  • Reading Notes

    • Effective C++

      • 习惯C++
      • 构造,析构, 和重载赋值运算符
        • 资源管理
        • 设计与声明
        • 实现Implementations
        • 继承与OOP
        • 模板,Templates and GP
        • 定制new和delete
        • 杂项, Misc
      • More Effective C++

      • 《C++ 性能优化指南》
    • Wiki
    • Reading Notes
    • Effective C++
    Quincy Jet
    2022-06-27
    Content

    构造,析构, 和重载赋值运算符

    # Item 05:Know what functions C++silently writes and calls. 了解C++默默编写并调用哪些函数

    1. C++ 自动生成的ctor,dtor, copy assignment(op=重载),(big three, 除此之外还有移动构造, 移动赋值, 不过这需要自己的写)

      class Empty{
          public:
          	Empty() { ... }
          	Empty(const Empty& rhs){ ... }
          	~Empty() { ... }
      
      	    Empty& operator=(const Empty& rhs) { ... }
      }
      
      int main(){
          Empty e1;
          Empty e2(e1);
      
          e2 = e1;
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      1. 默认的构造, 析构函数, 只能是浅复制, 值的复制, 如果有指针, 只能指向同一个地址, 而不能有两份内存出来;

      2. 对const成员变量, reference变量, 自动生成的copy assignment是不能对这些东西进行赋值和拷贝操作的

        class Timekeeper{
        public:
        	Timekeeper();
        	virtual ~TimeKeeper();
        }
        
        1
        2
        3
        4
        5
      3. 经验: class里面如果有一个virtual的函数, 就为这个类弄一个virtual的析构函数;

      4. 纯虚函数?纯虚函数会带来一个抽象基类, 抽象基类不能实例化;但是抽象基类要有一个纯虚的析构函数,需要在H文件里面单独提供一份定义;这样在析构的时候就不会显式运行到在这个纯虚函数;

        让其他的子类在析构的时候, 会使用到子类自己的析构函数, 而不是默认的析构, 或者是纯虚基类的析构(纯虚基类的纯虚析构函数, 是空的)

        class AWOV(){
        public:
        	virtual ~AWOV() = 0;
        }
        
        AMOV::~AMOV() { } // 在.h文件里面单独提供一份定义, 因为虽然是纯虚函数, 但是子类在析构的时候,还是会调用到这个
        
        1
        2
        3
        4
        5
        6

      # Item 06:Explicitly disallow the use of compiler-generated functions you do not want. 若不想使用编译器自动生成的函数,就该明确拒绝

    2. 如何拒绝使用这些自动生成的函数,

      1. 使用=delete来禁止使用拷贝构造, 拷贝赋值,
      2. 以前在没有=delete关键字的时候, 可以把这些函数放在private里面

      # Item 07:Declare destructors virtual in polymorphic base classes. 为多态基类声明virtual析构函数

    3. 多态基类的析构函数最好弄成virtual的, 如果是non-virtual的,那么借由基类指针传进来的对象, 析构的时候, 派生出来的部分会被漏掉

      1. 如果析构函数不是virtual的, 那么delete基类指针来进行析构的话。derived部分偶尔会被遗漏掉;
      2. 为什么不是virtual的构造函数?因为我们构造的时候, 基本上都是用derived类来进行构造的;

      问题: 对于virtual function, 如何进行派生使用, 如何借由virtual函数来进行多态?

    # Item 08:Prevent exceptions from leaving destructors. 别让异常逃离析构函数

    1. 析构函数不要吐出异常(别让异常逃离析构函数), 为什么?

      1. 如果不处理异常, 会带来一些不明确的行为, 或者程序半路断掉
      2. 所以要好好善后;怎么办,如果析构函数真的抛出异常, 就在析构函数里面进行try{} catch{} , 要么记录下来,立即停止程序,不重要的异常可以不停下来, 让程序继续进行;也可以用单独的函数来处理异常, 但总之就是在类内部, 处理好异常, 不要让异常逃出析构, 这样可以封装得更完整

    # Item 09:Never call virtual functions during construction or destruction. 绝不在构造和析构过程中调用virtual函数

    1. 构造和析构函数中, 不要调用virtual函数, 为什么?, static函数相关的使用场景和效果?

      1. 构造的时候, derived对象还没有生成, 所以这个时候还用不到自身特化的virtual function, 只能用base class里面的版本;
      2. 析构的时候, 只要一开始析构, 对象内的derived class成员变量就是未定义值, 所以这个时候virtual function还是会跑到父类里面去;
      3. 父类里面的static函数, 有什么作用? 是在函数编译的时候, 放在什么段里面?(很大程度和python的static method类似, 能通过类名调用) static函数里面不能调用non-static成员变量, 也不能调用non-static成员函数; static函数没有this指针

    # Item 10:Have assignment operators return a reference to*this. 令operator=返回一个 reference to *this

    1. operator= 最好是返回一个 reference to *this,

      1. 因为这样可以拥有连续赋值的特性, 例如: x = y = z = 15

    # Item 11:Handle assignment to self in operator=. 在operator=中判断“自我赋值”

    Widget& Widget::operator=(const Widget& rhs){
    	if(this == &rhs)
            return *this;
    	delete pb;
    	pb = new Bitmap(*rhs.pb);
    	return *rhs;
    }
    
    1
    2
    3
    4
    5
    6
    7

    # Item 12:Copy all parts of an object. 复制对象时勿忘其每一个成分

    1. 自定义拷贝构造函数的时候, 要复制所有local的变量, 可以使用基类的一些拷贝构造函数对一些成员拷贝;

      因为有一些基类对象的拷贝, 必须要通过基类的拷贝构造函数来进行深度拷贝;

      PriorityCustomer::operator=(const PriorityCustomer &rhs){
          logCall("......");
          Customer::operator=(rhs); // 这里直接调用了base class的拷贝构造函数来进行;
          priority = rhs.priority;
          return *this;
      }
      
      1
      2
      3
      4
      5
      6

      不要使用copy assignment 来调用copy constructor; 反过来也不建议, 因为这就像对一个构造好的对象进行操作, 先有鸡还是先有蛋?

      最终还是要将他么共同的一些实际操作放到函数里面, 因为只要使用上面两种函数, 都会要实现构造一个临时的对象, 但对象在这些函数运行过程中都是没有构造好的;

    习惯C++
    资源管理

    ← 习惯C++ 资源管理→

    Copyright © 2017-2023 Quincy Jet | MIT License
    • Auto
    • Light
    • Dark
    • Read