• 高效C++:构造/析构/赋值


    了解C++默认提供和调用的函数

    • 编译器会自动为每一个空类创建构造函数、拷贝构造函数、赋值运算符以及析构函数

    • 不要使用编译器自动创建的函数,要杜绝这种情况发生,自己编写这些函数

    如果不想使用编译器自动生成的函数,要明确拒绝

    • 编译器默认提供的函数,不仅不能给编程带来便利,而且会给在某种情况下引入难以定位的bug,因此应该明确拒绝

    • 拒绝的方式就是明确声明该函数为私有且不提供函数体

      • 私有表示对权限的控制,防止外部在意想不到的情况下调用

      • 不提供函数体,是让编译器在链接的时候报错,明确提示你注意这个函数

    class Uncopyable{
    private:
        Uncopyable(const Uncopyable&);
        Uncopyable& operator=(const Uncopyable&);
    }
    //作为基类继承即可

     

    为多态的基类声明virtual析构函数

    • 如果一个类中有一个虚函数,那么必须将析构函数也申明成虚函数,主要是防止资源泄露(使得析构从下向上依次执行)

    • 如果涉及的类不用于多态,那么就不要声明虚函数,因为虚函数的调用需要借助虚函数列表,调用的效率没有普通函数高

    • 如果不想一个类被实例化,那么就声明该类的虚函数为纯虚函数,且提供其实现

    class AbstractClass
    {
    public:
        virtual ~AbstractClass();
    }
    
    AbstractClass::~AbstractClass()
    {
        //函数实现
        //目的是进行资源释放
    }

    把异常消灭在析构函数中

    • 析构函数绝对不要吐出异常,这会导致资源部分释放的问题,析构函数中的异常一定要try...catch处理

    • 如果客户需要对某个操作做出反应,且有可能抛出异常,那么就需要提供一个普通函数执行该操作,这样设计的好处:

      • 提供普通函数给客户使用,且明确会抛出异常,使得用户有机会对异常做出合理的处理

      • 析构函数中检查资源释放的状态,做最后的处理

    class DBConn
    {
    private:
        DBConnect dbc;
        bool closed;
    public:
         void close()
        {
            dbc.close();
            closed = true;
        }
        
        ~DBConn()
        {
            if(!closed)
            {
                try
                {
                    dbc.close();
                } catch(...) {
                    //停止或记录
                }
            }
        }  
    }
    //解决close会跑出异常的可能

     

    绝不在构造函数和析构函数中调用virtual函数

    • 看清楚描述,是构造函数和析构函数中不能调用虚函数;析构函数本身是可以设计成虚函数的,且在某些情况下,必须设计成虚函数

    • 在构造函数未执行完成之前,类的虚函数表未建立

    • 进入析构函数之前,类的虚函数表已经销毁,无法调用

    让operator=返回一个reference to *this

    好处:便于连续赋值,类似下面这种执行方式:

    a = b = c = d //连续赋值
    class Widget
    {
    public:
        Widget& operator+=(const Widget& rhs)
        {
            return *this;
        }
    }

    在operator=中处理自我赋值

    • 确认任何函数操作一个以上的对象,且对象可能重复,那么需要进行自我赋值的校验

    • 进行自我赋值的判断:地址判断、copy-and-swap

    • copy-and-swap:交换两个对象的数据,在交换的过程中,任意一句语句发生异常,只会导致交换函数本身交换不成功,不会导致程序发生其他异常,程序依然可以运行,状态回到没有调用交换之前,唯一遗憾的就是程序的业务逻辑错了。这种函数我们叫异常安全函数

    Widget& operator=(const Widget& rhs)
    {
        if(this == &rhs)
            return *this;
        // ...
        return *this;
    }
    
    Widget& operator=(const Widget& rhs)
    {
        Widget temp(rhs);
        swap(temp);
        return *this;
    }
    
    Widget& operator=(Widget rhs)  //拷贝发生在参数传入的过程中
    {
        swap(rhs);
        return *this;
    }

     

    赋值对象时不要忘记其中的每一个成员

    • 代码静态检查时,有一条规则就是需要初始化每一个成员变量

    • 拷贝构造函数和赋值运算符不能相互调用---记住就好,如果有相同的代码可以提取成一个private函数

    • 拷贝构造的时候一定要拷贝自己的每一个成员,包括基类的每一个成员

  • 相关阅读:
    Gitlab使用腾讯企业邮箱
    查看Binlog内容
    微信小游戏手记
    clickhouse手记
    腾讯云手记
    go框架gin
    go idea debug
    go手记
    crontab手记
    Laravel-cors 跨域
  • 原文地址:https://www.cnblogs.com/chusiyong/p/11448264.html
Copyright © 2020-2023  润新知