• EC++学习笔记(二) 构造/析构/赋值


    条款05:了解c++默默编写并调用了哪些函数

    编译器可以暗自为 class 创建default构造函数,copy构造函数,copy assignment操作和析构函数
    所有这些函数都是 public 并且 inline
    编译器自动生成的析构函数是non-virtual
    编写一个空类 class Widget{};
    相当于:

    class Widget{
    public:
        Widget() {...}
        ~Widget() {...}
        Widget(const Widget& rhs) {...}
        Widget& operator=(const Widget& rhs) {...}
    };

    注意:这些函数仅当被需要(被调用),他们才被编译器创建出来
    自动生成的copy构造函数和 copy assignment操作符 只是单纯地将来源对象的每一个non-static 成员变量拷贝到目标对象

    三法则:如果一个类需要编写显式析构函数,那么同时需要显式编写copy构造函数和 copy assignment操作符
    如果一个类内含 reference成员变量 或内含 const 成员变量时,应该显式编写copy构造函数和 copy assignment操作符
    因为 reference变量始终绑定在初始化时的变量之上,不允许更改绑定对象, const 变量也不能因为赋值操作被更改

    如果其中声明了一个构造函数,编译器于是不再为它创建default构造函数

    条款06:若不想使用编译器自动生成的函数,就该明确拒绝

    如果一个类中含有指针变量文件描述符(两者都是资源对象,内存资源和文件资源,具有 引用语义,即复制操作之后,具有引用语义的对象并没有与原对象分离,而是共同指向同一资源,注意区别于值语义对象)

    如果该类具有引用语义,不能使用编译器自动生成的函数(copy构造函数和copy assignment操作符),那么就将这两个函数声明为private并且不予实现

    class Widget{
    private:
        Widget(const Widget& rhs);
        Widget& operator=(const Widget& rhs);
    };

    如果发生Widget对象拷贝的情况,编译器会阻止,如果类内部成员函数调用这两个函数,链接器会报错(因为只有声明却没有定义)

    注: boost::noncopyable

     可以将下列宏定义置于 class的 private部分

    #define DISALOW_COPY_AND_ASSIGN(TypeName) \
        TypeName(const TypeName&); \
        TypeName& operator=(const TypeName&)
    条款07:为多态基类声明 virtual 析构函数

    当derived class 对象经由一个base class 指针被删除,且该base class 带着一个non-virtual 析构函数时,其结果未有定义。
    原因:因为是base class 指针,所以指针按照 base class 的析构函数的方式进行解释(即使是该指针指向derived class,但是函数是non-virtual 的,多态不成立)
    这样,derived class 内部继承而来的base class 部分被析构掉,而自身原有的部分仍然存在,造成了"局部销毁"
    解决方法:给base class 一个 virtual 析构函数(利用多态)

    任何 class 只要带有 virtual 函数都几乎确定应该也有一个 virtual 析构函数

    所有的标准容器例如 vector,list,string,set,map都不带有 virtual 析构函数 且不作为 base class 使用
    所以:禁止继承一个标准容器类或者带有non-virtual 析构函数的 class

    条款08:别让异常逃离析构函数

    析构函数绝对不要吐出异常(容易造成资源泄露),如果程序遭遇一个"于析构函数期间发生的错误"后无法继续执行,调用abort"强迫结束程序"是个合理选择,这样可以阻止异常从析构函数传播出去

    条款09:绝不在构造和析构过程中调用 virtual 函数

    derived class 对象内的base class 成分会在 derived class 自身成分被构造之前先构造妥当

    对象在derived class 构造函数开始执行前不会成为一个derived class,此时对象会被解析至 base class对象,virtual 函数只会执行base calss对应的那份函数
    如果base class 构造函数调用 virtual 函数时,此时 virtual 函数会被编译器解析至base class,而不会下降至derived class

    条款10:令 operator= 返回一个reference to *this

    这样就可以连续赋值了,比如 a  = b = c

    class Widget{
    public:
        Widget& operator=(const Widget& rhs) {
            ...
            return *this;
        }
    };

    这个协议不仅适用于标准赋值形式,也适用于所有赋值相关的运算,例如+=,*=

    条款11:在 operator=中处理"自我赋值"

    "自我赋值"发生在对象被赋值给自己时
    一般而言,如果某段代码操作pointer或reference,而它们被用来"指向多个相同类型的对象",就需要考虑这些对象是否为同一个

    class Base{...};
    class Derived : public Base{...};
    void DoSomething(const Base& rb, Derived* pd);    //rb和*pd可能其实是同一对象(基类类型的指针或引用,可以引用基类类型对象,也可以引用派生类类型对象)

    如果使用对象来管理资源,资源管理对象在copy发生时有正确的举措,自我赋值是安全的

    class Bitmmap{...};
    class Widget{
    private:
        Bitmmap* pb;
    };
    Widget& Widget::operator=(const Widget& rhs) {
        delete pb;
        pb = new Bitmmap(*rhs.pb);
        return *this;
    }

    上面赋值操作符中,*this 和 rhs有可能是同一对象,delete pb之后,rhs.pb会发生指向野指针

    修改为:

    Widget& Widget::operator=(const Widget& rhs) {
        if (this == &rhs) return *this;
        delete pb;
        pb = new Bitmmap(*rhs.pb);
        return *this;
    }

    copy and swap 技术:

    pass by reference:

    Widget& Widget::operator=(const Widget& rhs) {
        Widget tmp(rhs);
        swap(rhs);
        return *this;
    }

    pass by value

    Widget& Widget::operator=(Widget rhs) {
        swap(rhs);
        return *this;
    }
    条款12:复制对象时勿忘其每一个成分

    用户自定义的 copy函数必须复制对象内的每一个成员变量
    在为derived class 编写 copy函数时,必须很小心的也复制其 base class 成分,那些成分往往是 private,所以让derived class 的copy函数调用相应的base class 函数的copy函数

    Derived::Derived(const Derived& rhs)
           : Base(rhs), member(rhs.member) { }
    
    Derived& Derived::operator=(const Derived& rhs) {
        Base::operator=(rhs);
        member = rhs.member;
        return *this;
    }

    copy函数应该确保复制"对象内的所有成员变量"和"所有base class成分"

  • 相关阅读:
    二叉树的建立和遍历
    canvas基础入门(一)canvas的width、height于css样式中的宽高区别
    js实现放大镜效果
    ios中页面底部输入框,position:fixed元素的问题
    js input复选框选中父级同时子级也选中
    js apply和call
    javascript闭包理解
    vue.js vue-jsonp解决跨域问题
    vue.js请求数据(axios)
    vuex
  • 原文地址:https://www.cnblogs.com/wwwjieo0/p/3442953.html
Copyright © 2020-2023  润新知