• EC++学习笔记(四) 设计与声明


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

    必须考虑客户可能做出什么样的错误(防御式编程)
    std:shared_ptr会自动使用它的"每个指针专属的删除器",消除了"cross-DLL problem"(对象在DLL中被 new 创建,却在另一个DLL中 delete 销毁)

    条款19:设计 class 犹如设计 type 类型系统
    条款20:宁以 pass-by-reference-to-const 代替 pass-by-value

    C语言永远是pass-by-value,C++默认是pass-by-value,函数参数都是以实际参数的副本拷贝为初值,函数调用的结果是函数返回值的一个副本拷贝
    注意C语言中传指针方式,传指针具有引用语义,对象复制以后并没有分离,而是共享关联同一资源
    采用pass-by-reference-to-const 效率高(无对象拷贝),是多态前提(基类引用),也可以避免基类和派生类之间的对象切割

    条款21:必须返回局部对象时,禁止返回其reference
    const Rational& operator*(const Rational& lhs, const Rational& rhs) {
        Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
        return result;
    }

    这个函数返回一个reference指向result,但result是local对象,local对象在函数退出前已经被销毁了!!!

    任何函数如果返回一个reference指向某个local对象都将一败涂地
    任何函数如果返回一个指针指向某个local对象同样将一败涂地

    const Rational& operator*(const Rational& lhs, const Rational& rhs) {
        static Rational result;
        result = ...;
        return result;
    }

    这个函数返回static对象,不具有多线程安全性,并行计算时极易出错

    必须返回对象时,就让函数pass-by-value返回一个新对象

    inline const Rational operator*(const Rational& lhs, const Rational& rhs) {
        Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
        return result;
    }

    这个函数以pass-by-value方式返回值语义对象,值语义对象复制之后两者分离,所以函数结束后获得的仍然是正确的对象

    总结:绝不要返回reference或pointer指向一个local对象

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

    客户唯一能够访问对象成员变量的方法就是通过存取函数get()和set()
    成员变量的封装性与"成员变量的内容改变时所破坏的代码量"成反比

    条款23:宁以non-member、non-friend 替换member函数(本人不认同)
    条款24:操作符重载时必须选择设置为类成员还是普通的非成员函数

    操作符重载时,选择成员还是非成员实现有一些指导原则:
    1.赋值(=)、下标([])、调用( () )、成员访问(->)必须定义为类成员,否则编译出错
    2.复合赋值操作符(+=)通常定义为类成员
    3.对称操作符(算术运算符、关系运算符)通常选择为non-member函数实现
    注:操作符选择类成员实现时,显式形参数比操作数数目少1,因为当操作符为成员函数时,this 指向左操作数

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

    STL提供的标准swap算法如下:

    namespace std {
        template<typename T>
        void swap(T& a, T& b) {
            T temp(a);
            a = b;
            b = temp;
        }
    }

    缺省版本设计到对象复制,有时效率低下。但是考虑如下:

    class WidgetImpl{
    public:
        ...
    private:
        int a, b, c;
        std::vector<double> v;
    };
    
    class Widget{
    public:
        Widget(const Widget& rhs);
        Widget& operator=(const Widget& rhs) {
            ...
            *pImpl = *(rhs.pImpl);
        }
    private:
        WidgetImpl* pImpl;
    };

    一旦置换两个Widget对象,唯一需要做的就是置换其pImpl指针,可以考虑模板特化技术

    namespace std{
        template<>
        void swap<Widget>(Widget& a, Widget& b) {
            swap(a.pImpl, b.pImpl); //目前还无法编译通过
        }
    }

    上述代码中 template<> 表示它是 std:swap的一个全特化版本,函数名之后的<Widget>表示这一特化版本只针对"T是Widget"而设计(偏特化)
    上述代码不能通过编译,因为a.pImpl和b.pImpl都是 private,所以进行如下改进:

    class Widget{
    public:
        ...
        void swap(Widget& other) {    //绝不可以抛出异常
            using std::swap;          //逼迫编译器使用std::swap(因为偏特化版本swap还未实现完成)
            swap(pImpl, other.pImpl); //此时使用的是std::swap
        }
    }
    
    namespace std{
        template<>
        void swap<Widget>(Widget& a, Widget& b) {
            a.swap(b);
        }
    }

    一旦编译器看到对swap调用时,就会查找适当的swap调用,如果没有T专属之swap存在,编译器就会使用std内的swap

    std::swap(obj1, obj2);    //错误的swap调用方式,强迫编译器总是调用std内部的swap版本
    swap(obj1, obj2);         //正确的swap调用方式,不带任何的命名空间修饰

     c++只允许对类模板进行偏特化,不能对函数模板进行偏特化

  • 相关阅读:
    不用服务器也能跑的框架wojilu
    PLI 到 COBOL 的转换数据类型 【不搞Mainframe的可能看不懂,冷门的语言】
    我记录网站综合系统 技术原理解析[8:ActionChecker流程]
    纪念VB.NET君
    我记录网站综合系统 技术原理解析[7:CSS类]
    我记录网站综合系统 技术原理解析[10:PermissionChecker流程]
    我记录网站综合系统 技术原理解析[6:内容初始化处理]
    我记录网站综合系统 技术原理解析[9:HttpMethodChecker流程]
    怎么打败腾讯[纯讨论]
    VisualStudio2012新特性[路边社通稿]
  • 原文地址:https://www.cnblogs.com/wwwjieo0/p/3443256.html
Copyright © 2020-2023  润新知