zoukankan      html  css  js  c++  java
  • effective c++ 笔记 (5-8)

    //---------------------------15/03/26----------------------------



    //#5    了解c++默默编写并调用哪些函数

    {

       /*

            c++会默默帮你声明一个copy构造函数,一个copy assignment操作符重载,

            一个析构函数,一个default构造函数。并且都是 public inline

            因此如果写下:

        */

       class Empty{};

        //就好像写下这样的代码:

        

       class Empty

        {

        public:

            Empty(){...}

            Empty(const Empty& rhs) {...}

            ~Empty(){...}

            Empty&operator=(const Empty& rhs){...}

        };

        

        //1:这些函数,只有被需求(被调用)时,才会被编译器创建出来

        

        /** 

          2:copy构造函数和copy assignment操作符,编译器创建的版本只是单纯地

            将来源对象的每一个non_static成员拷贝到目标对象中去 

        **/

        

        //3:copy的时候:如果成员变量有copy构造函数,编译器就调用其copy构造函数,

        //如果没有copy构造函数,就会拷贝每一个bits 来完成初始化

        

       /*

          4:在什么情况下编译器拒绝帮忙定义一个copy assignment

            1>对象内包含一个reference变量,原因:reference自身不可以被再次指向另一个值

            如果你想让这个reference的内容改变成目标对象的内容,那需要自行实现operator=操作

            其实也就是深拷贝。

            2>对象内包含一个const变量,原因:更改const变量的值时不合法的。

            3>如果base classescopy assignment操作符声明为private。原因:原本derived classes

            copy assignment操作符是可以处理base class成分的,但是现在他们无法调用了。

            编译器就两手一摊,无能为力了。

        */

        

        

    }


    //#6    若不想使用编译器自动生成的函数,就该明确拒绝

    {

       /*

            当你想使你的对象不能被复制时(比如每个人的资产都是私有的,不该被复制给别人)

            你有两个方法可以做到:                                                 */

        //  1>自己声明一个privatecopy构造函数以及copy assignment操作符。

       class HomeForSale

        {

        public:

            ...

        private:

            ...

            HomeForSale(const HomeForSale&);

            HomeForSale&operator=(const HomeForSale&);

        };

        // 这样就声明了这两个函数,要注意的是,不要去实现它,这样可以在别人

        // 使用这两个函数时报错(编译器报错),当成员函数或friend函数使用时,会产生连接时报错

        //  2>为了时报错提早(所有报错都由编译器产生),可以专门设计一个base类:

       class Uncopyable

        {

        protected:

            Uncopyable(){}

            ~Uncopyable(){}

        private:

            Uncopyable(const Uncopyable&);

            Uncopyable&operator=(const Uncopyable&);

        };

       /*

            这样只要想阻止copying行为,只需要继承Uncopyable类就可以了

            这里会产生几个微妙的实现和运用

            1>不一定需要使用public继承

            2>Uncopyable的析构函数可以不是virtual(这个类不是接口,只能算是一个属性)

            3>Uncopyable中不含数据,符合一个empty base class optimization的资格

            4>由于常常被继承,很可能造成多重继承。

        */

        //总结:如果你的类常需要从多个基类继承,那最好自己声明两个copy函数,否则,不妨从

        //Uncopyable那继承。

        

    }


    //#7    为多态基类声明virtual析构函数

    {

       /*

            这一点,不必多说,只要了解c++多态,都知道要给多态基类声明一个virtual析构函数防治内存泄漏

            这个条款中比较新鲜的东西有两个:

            1:不要为一个非多态用途的类声明virtual析构函数。原因是:

                1>这么做会使的对象占用内存变大,

                尤其是对于一些小巧的类来说,本来可以直接放在一个寄存器中的,一声明vitual属性,

                32位系统中就变大了32bits,在64位系统中就变大了64bits。这个大小就是一个指针的大小,

                这个指针就是vptr指针,指向虚函数表,里面存放着由函数指针 构成的数组。

                2>由于vptr指针,这个对象将变得没有移植性。因为其他语言没有vptr这东西。

            2:如果想使一个base class获得不能被实例化 的特性,而这个base calss中又没有

            pure vitual函数(纯虚函数),可以把析构函数设置为pure vitual函数。值得注意的是

            你还是要为这个析构函数提供一份定义。因为基类析构函数总是会在其派生类调用时被调用。

         

        */

    }


    //#8    别让异常逃离析构函数

    {

        // 如果析构函数可以吐出异常,会造成程序的结束执行或不明确行为,看下面的例子:

       class Widget

        {

        public:

            ...

            ~Widget(){...}

        };

       void doSomething()

        {

            std::vector<Widget> v;

        }

        

       /*

            vector被销毁时,vector会依次调用其中的所有widget对象(假设有10),如果析构允许

            抛出异常的话,析构第一个的时候,抛出了异常,为了确保不会发生内存泄漏,必须继续析构其他

            九个widget,如果第二个异常又被抛出,现在有两个同时作用的异常,在两个异常同时存在的情况下,

            程序不是结束执行就是导致不明确行为。

                                                                        */

        //如果析构函数必须执行一个动作,而该动作可能在失败时抛出异常,那么应该这么做

        //给客户提供一个动作,让他自己去调用,并在析构函数中检查是否完成,并吞下异常或终止程序

       class DBConn

        {

        public:

            ...

           void close()

            {

                db.close();

                closed =true;

            }

            ~DBConn()

            {

               if(!closed)

                {

                   try

                    {

                        db.close();

                    }catch (...)

                    {

                       //记下对close调用失败的log

                       //选择是否中止

                    }

                }

            }

        private:

            DBConnection db;

           bool closed;

        };

        //这样可以给客户一个调用close的机会,如果他自己不处理,也没法抱怨设计这个类的我们了。

        

    }















  • 相关阅读:
    160. Intersection of Two Linked Lists
    155. Min Stack
    TensorRT caffemodel serialize序列化
    141. Linked List Cycle
    异或运算的性质及应用
    136. Single Number
    【leeetcode】125-Valid Palindrome
    c++函数参数类型-引用、指针、值
    【linux基础】linux远程登录SSH
    【leetcode】122-Best Time to Buy and Sell Stock II
  • 原文地址:https://www.cnblogs.com/boydfd/p/4983158.html
Copyright © 2011-2022 走看看