• 《Effective C++》设计与声明:条款18-条款25


    条款18:让接口容易被正确使用,不容易被误用

    • 注意使用const,explicit,shared_ptr等来限制接口。
    • 必要时可以创建一些新的类型,限制类型操作,束缚对象等。
    • 注意保持接口的一致性,且与内置类型的行为兼容。
    class Month {
    public:
        static Month Jan(){return Month(1);}
        static Month Feb(){return Month(2);}
        ...
        static Month Dec(){return Month(12);}
    private:
        explicit Month(int m) : val(m){}
        int val;
    };
    

    条款19:设计class犹如设计type

    • 对象的创建和销毁
      • 构造函数和析构函数的设计
    • 对象的初始化和赋值
      • 构造函数和赋值操作符的设计及其之间的差别
    • 对象被以值传递(pass by value)
      • copy构造函数的设计
    • 有效的对象
      • class中一些成员变量是受约束的,决定了成员函数(特别是构造函数,赋值操作符和所谓的"setter"函数)必须进行检查工作。
    • 新type用于继承中
      • 继承自现有classes,就会受到这些classes的束缚,特别是受它们"virtual"和"non-virtual"成员函数的影响。
      • 被继承,要考虑所声明的函数(特别是析构函数)是否为virtual
    • 新type的转换
      • 类型T1隐式转换为类型T2,可在class T1中写一个类型转换函数(operator T2() const)或在class T2中写一个构造函数(T2(T1))
    • 为type设计合理的操作符和函数
    • 一些函数要被驳回不可调用(设置为private)
    • 判断定义的type是否过于泛化
      • 可写成class template
    • 判断是否真的需要定义新的type
      • 可写成新的drive class,non-member,templates以取代

    条款20:宁以pass-by-reference-to-const替换pass-by-value

    • 尽量以pass-by-reference-to-const替换pass-by-value,效率高效,可以避免对象切割问题。
    • 对于内置类型,STL迭代器,和函数对象,"pass-by-value并不昂贵",采用pass-by-value更合适(其实采用pass-by-reference-to-const也可以)。

    条款21:必须返回对象时,别妄想返回其reference

    • 不要返回pointer或者reference指向一个on stack对象
      • 局部对象被析构导致异常
    • 不要返回pointer或者reference指向一个on heap对象
      • 需要用户delete,可以选择返回shared_ptr
    • 不要返回pointer或者reference指向local static对象
      • static只能有一份,但可能返回多个对象
      • 确保只用一份时可以使用
        以上约束在必要时可以违背,非绝对。

    条款22:将成员变量声明为private

    • 切记将成员变量申明为private
    • protected并不比public更有封装性(用户可能继承你的base class)

    条款23:宁以non-member、non-friend替换member函数

    • 多一个成员函数,就多一分破坏封装性
    • 当non-member与member函数实现相同功能时,使用non-member函数
    • 尽量不要用友元

    条款24:若所有参数皆需要类型转换,请为此采用non-member函数

    • 如果要求成员函数要对this指针所指的自身对象进行类型转换,那么只能将这个函数改为非成员函数
    • 其实质上是member函数没有办法转换this指针所指自身对象的类型
    • 也尽量不要用友元会破坏封装性的

    条款25:考虑写出一个不抛出异常的swap函数

    • std::swap调用copy构造函数和赋值操作符,效率较低,因此可以自己提供swap函数
    namespace std {
        template<typename T>
        void swap(T& a, T& b) {
            T temp(a);
            a = b;
            b = temp;
        }
    }
    
    • classes 可以提供一个member swap,再提供一个特化std::swap用来调用前者
    #include <iostream>
    #include <string>
    #include <vector>
    
    class WidgetImpl {
    public:
        WidgetImpl( int _a, int _b, int _c, std::vector<double> _v) :
             a(_a), b(_b), c(_c), v(_v) {}
        int geta(){return a;}
    private:
        int a, b, c;
        std::vector<double> v;
    };
    
    class Widget {
    public:
        Widget(WidgetImpl* _p) : pImpl(_p) {}
    
        Widget(const Widget& rhs) {
            *pImpl = *(rhs.pImpl);
        }
        void swap(Widget& other) {
            std::cerr << "member swap" << std::endl;
            using std::swap;
            swap(pImpl, other.pImpl);
        }
        WidgetImpl* getptr() {return pImpl;}
    private:
        WidgetImpl* pImpl;
    };
    
    namespace std {
        template<>
        void swap<Widget>(Widget& a, Widget& b) {
            std::cerr << "std special swap" << std::endl;
            a.swap(b);
        }
    }
    
    
    int main()
    {
        std::vector<double> v1(0.,10);
        std::vector<double> v2(1.,10);
        WidgetImpl a1(0,0,0,v1) , a2(1,1,1,v2);
        Widget b1(&a1), b2(&a2);
        std::swap(b1, b2);
        std::cerr << b1.getptr()->geta() << std::endl;
    }
    

    • classes 和 class templates 可以提供一个member swap,再提供一个non-member swap用来调用前者
    #include <iostream>
    #include <string>
    
    template<typename T>
    class WidgetImpl {
    public:
        WidgetImpl(T _t, int _a, int _b, int _c, std::vector<double> _v) :
            t(_t), a(_a), b(_b), c(_c), v(_v) {}
        T getT(){return t;}
    private:
        T t;
        int a, b, c;
        std::vector<double> v;
    };
    
    template<typename T>
    class Widget {
    public:
        Widget(WidgetImpl<T>* _p) : pImpl(_p) {}
    
        Widget(const Widget& rhs) {
            *pImpl = *(rhs.pImpl);
        }
        void swap(Widget& other) {
            std::cerr << "member swap" << std::endl;
            using std::swap;
            swap(pImpl, other.pImpl);
        }
        WidgetImpl<T>* getptr() {return pImpl;}
    private:
        WidgetImpl<T>* pImpl;
    };
    
    template<typename T>
    void swap(Widget<T>& a, Widget<T>& b) {
        std::cerr << "global swap" << std::endl;
        a.swap(b);
    }
    
    
    int main()
    {
        std::vector<double> v1(0.,10);
        std::vector<double> v2(1.,10);
        WidgetImpl<std::string> a1("str1", 0,0,0,v1) , a2("str2",1,1,1,v2);
        Widget<std::string> b1(&a1), b2(&a2);
        swap(b1, b2);
        std::cerr << b1.getptr()->getT() << std::endl;
    }
    

  • 相关阅读:
    vue+mongoose+node.js项目总结第三篇_实现评论和回复功能
    vue+mongoose+node.js项目总结第二篇_模拟动态发布功能
    vue+mongoose+node.js项目总结第一篇_图片文件上传
    项目(踩坑)_vue中使用input file类型的时候获取不到当前的inputFile对象
    项目(踩坑)_node 中使用session中间件报undefined的问题
    netstat
    ifconfig
    mount/umount命令
    df du sync
    ln
  • 原文地址:https://www.cnblogs.com/narjaja/p/10179903.html
Copyright © 2020-2023  润新知