《More Effective C++》- 极精简版 21-30条

        本文章属于专栏《业界Cpp进阶建议整理》


          继续上一篇《More Effective C++》- 极精简版 11-20条。本章我会继续讲解我对21-30条的极精简的理解。

  • 21、利用重载技术避免隐式转换
    • const A operator+(const A& a1, const A& a2) ,且有构造函数 A(int){}
    • 那 A a3 = 2 + a2;时,会先将2隐式转换为A类型的临时变量,然后调用函数。
    • 这里可以通过增加 const A operator+(int a1, const A& a2)来防止隐式转换产生临时变量
    • 但是注意,重载操作符,至少获取一个用户定制类型,不能是const A operator+(int a1, int a2)
    • 个人见解:在业务代码中,尽量使用explicit不要让隐氏转换发生。
  • 22、使用操作符,考虑用复合形式(op=)替换独身形式(op)
    • 如 +=是在自身对象上操作,没有临时对象的构造和析构。所以尽量用+=
    • 当然有时候为了代码的可读性,使用 A a1 = a2 + a3 + a4 + a5;也是可以的,在于这段代码的使用频率,是不是性能优化的重点
    • 另外为了,代码逻辑方便维护,实际加法逻辑只有一份, operator+ 调用operator+=是一个很有意义的方法。
  • 23、考虑使用其他程序库
    • 个人见解:在用三方库时,不受惯性思想,搜一下当前时间点,性能最好,最稳定的库
  • 24、了解virtual functions、multiple inheritance、virtual base classes、runtime type identification成本
    • 虚函数成本:
      • 虚函数表:每个包含虚函数的类都有一个虚函数表,表中存的是指向虚函数的地址,虚函数表的大小和虚函数个数相关。
      • 即使是多继承、或者virtual继承,虚函数本身的开销不会更大,反而是它不能inline关系更大
      • 虚函数放弃了inline(两个语义本身就冲突,虚函数运行时调用,inline编译期用函数体替换)
      • 虚函数调用就是,通过指针或者引用,找到对象的虚函数表指针,然后找到需要调用的虚函数表中的虚函数指针,执行函数。
      • 虚函数指针:每个对象都有一个虚函数指针
    • RTTI成本:
      • 作用:运行时,获取objects和classes的信息。
      • 原理:存放在每个对象的type_info结构中,这个结构由编译器生成 。一个class只需要一份RTTI,这个结构在很多编译器的实现中,放到了虚函数表中
  • 25、虚拟化构造函数和非成员函数
    • 将std::vector<Base*>的内容复制到新的vector(这里是复制内容,不是指针),不能直接调用拷贝构造函数,因为不知道具体是哪个class,这里需要一个clone函数。如基类是Base* clone() {return *this} 派生类Derive* clone() {return *this}
    • 非成员函数虚拟化,是为了多态
  • 26、限制class能产生的数量
    • 生产0个:将构造函数放到private
    • 生产1个:写一个返回对象的static函数(有static成员的函数不能用inline,每个调用函数的地方,都可能有自己的static成员变量),函数中有一个static成员
    • 生产多个:class有自己的计数器,有自己的工厂函数,有一个自己的static成员,进行计数。
      • 用一个template基类来做(注意一定要用模板基类,static计数,被所有派生类公用)
  • 27、要求、禁止对象产生于heap中
    • 要求对象必须产生在heap中,将构造函数和析构函数放到protect(private中,会影响继承)
    • 但是这样的话,派生类可以生成在栈上,其基类部分自然也在栈上
    • 想要派生类也保证只在heap上声明,利用的是operator delete,让其只删除通过operator new出来的东西,覆盖operator new(让其只从堆申请内存,并存放到类的list结构中),而delete时,去判断delete的内容是否来自于堆。
      • 这样的做法,在编译期间没有办法发现,需要在运行时候,通过异常发现(由delete抛出)
    • 个人见解:这样的需求,对于业务代码来说,有些纸上谈兵,实际中要求对象必须生产在堆中或者不在堆中,由开发者来保证是十分容易的。这样的设计模式增加的维护代价,收益却不明显。
  • 28、智能指针
    • 构造、析构和赋值容易,注意赋值需要判断是否是自己。operator()和operator->一个返回实际对象,一个返回实际指针
    • 判断存的内容是否为nullptr,调用智能指针的.get(),
    • 与继承的关系,
      • shared_ptr<Base> 和shared_ptr<Derived>不是同一类型,也不能自动转换,所以当函数参数要<Base>时,传<Derived>是不能通过编译的。
        • 对于自己写的函数,可以考虑template,这样会对不同类型的智能指针,产生实例
        • 对于历史的,没有办法改变的函数,考虑做一个拷贝 <Base> b = d,然后传入
    • 与const的关系
      • 虽然const与非const不是一个类型,但是shared_ptr<Base> b; shared_ptr<const Base> cb = b;是可以的,shared_ptr已经内部实现了
    • 个人补充:多线程对同一个智能指针对象操作是不安全的,但是智能指针对象通过复制进入各个线程,则是线程安全的
  • 29、引用计数
    • 什么时候使用引用计数
      • 引用技术是有成本的,当管理的对象越小,其相对来说代价越大。只有当引用技术带来的构造和析构的成本小时,不成为性能瓶颈,且控制数据的申请释放比较困难,我们才更愿意使用(在有限的范围内,数量不是巨大的时候,引用技术是很有意义的)
  • 30、代理类
    • 通过代理类+类型转换,区分operator[]的读和写。以在读和写时进行不同的操作。如智能指针。下面是一个简单的区分原理代码
#include <iostream>

// 原始数据类
class Data {
private:
    int value;

public:
    Data(int val) : value(val) {}

    // 代理类
    class Proxy {
    private:
        Data& data;

    public:
        Proxy(Data& d) : data(d) {}

        // 读操作
        operator int() const {
            std::cout << "Reading value: " << data.value << std::endl;
            return data.value;
        }

        // 写操作
        Proxy& operator=(int newVal) {
            std::cout << "Writing value: " << newVal << std::endl;
            data.value = newVal;
            return *this;
        }
    };

    // 返回代理类对象
    Proxy operator[](int index) {
        // 可以在这里添加索引范围检查等逻辑
        return Proxy(*this);
    }
};

int main() {
    Data dataObj(42);

    // 通过代理类进行读操作
    int readValue = dataObj[0];
    std::cout << "Read result: " << readValue << std::endl;

    // 通过代理类进行写操作
    dataObj[0] = 100;

    return 0;
}

//运行结果
//Reading value: 42
//Read result: 42
//Writing value: 100

相关推荐

  1. 《More Effective C++》- 精简 21-30

    2024-03-10 14:26:03       22 阅读
  2. 《Effective Modern C++》- 精简 30-35

    2024-03-10 14:26:03       19 阅读
  3. 《Effective Modern C++》- 精简 36-42

    2024-03-10 14:26:03       19 阅读
  4. effective c++ 笔记 26-31

    2024-03-10 14:26:03       24 阅读
  5. 21-30图表

    2024-03-10 14:26:03       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-10 14:26:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-10 14:26:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-10 14:26:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-10 14:26:03       20 阅读

热门阅读

  1. 面试怎么介绍Dubbo

    2024-03-10 14:26:03       22 阅读
  2. 生成子序列和 有序的nlog(n) 算法

    2024-03-10 14:26:03       25 阅读
  3. rust引用-借用机制扩展

    2024-03-10 14:26:03       20 阅读
  4. MySQL 8.0 架构 之 DDL日志(元数据日志)(DDL log)

    2024-03-10 14:26:03       21 阅读
  5. Unity3D 实现大世界地图的技术原理详解

    2024-03-10 14:26:03       20 阅读
  6. IOS面试题object-c 1-10

    2024-03-10 14:26:03       23 阅读
  7. iOS面试题

    2024-03-10 14:26:03       22 阅读
  8. [论文笔记] Open-sora 2、视频数据集介绍 MSR-VTT

    2024-03-10 14:26:03       23 阅读
  9. android 快速实现 recyclerview 的所有item 都执行动画

    2024-03-10 14:26:03       21 阅读