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


    如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码就不该通过编译,如果代码通过了编译,

    它的作为就该是客户所想要的。

    class Date
    {
    public:
         Date(int month, int day, int year);
         ...
    };

    第一,以错误的次序传递参数:

    Date(30, 3, 1998);//应该是“3,30”而不是“30,3”

    第二,传递一个无效的月份或天数:(打岔一个键)
    Date(2, 30, 1998);//应该是“3,30”而不是“2, 30”

    许多客户端错误可以通过导入新的类型而获得预防:

    简单的外覆(wrapper types)类型来区别天数、月份、和年份,然后于Date构造函数中使用这些类型:

    struct Day{
        explicit Day(int d)
            : val(d){}
        int val;
    };

    struct Month{
        explicit Month(int m)
            : val(m);
        int val;
    };

    struct Year{
        explicit Year(int y)
            : val(y);
        int val;
    };

    class Date
    {
    public:
        Date(const Month& m, const Day& d, const Year& y);
        ...
    };

    Date d(30, 3, 1998); //错误,不正确类型
    Date d(Day(30), Month(3), Year(1998));//错误,不正确类型
    Date d(Month(3), Day(30), Year(1998));//正确,正确类型

    一旦正确的类型就定位,限定其值有时候是通情达理的:enums不具备我们希望的类型安全性;比较安全的做法是预先定义

    有效的Months:

    class Month
    {
    public:
        static Month Jan() {return Month(1);}
        static Month Feb() {return Month(2);}
        static Month Mar() {return Month(3);}
        ...
        static Month Dec() {return Month(12);}
    private:
        explicit Month(int m);
        ...
    };

    Date d(Month::Mar(); Day(3), Year(1998));

    预防客户错误的另一个办法是:限制类型内什么事可做,什么事不能做。常见的限制是加上const。

    另一个一般性准则是:“除非有好理由,否则应该尽量令你的types的行为与内置types一致”。一旦怀疑,就拿ints做范本。

    避免与内置类型不兼容,真正的理由是为了提供行为一致的接口,stl容器的接口十分一致,使得它们很容易被使用。

    任何接口如果要求客户必须记得做某些事情,就是有着“不正确使用”的倾向,因为客户可能会忘记做那件事。

    Investment* createInvestment();//必须要求客户删除返回的指针,

    //客户可能没有删除指针,或者删除指针超过一次。

    较佳的设计原则是先发制人,令factory函数返回一个只能指针:

    std::tr1::shared_ptr<Investment> createInvestment();

    如果设计者希望那些从createInvestment 取得的Investment*指针不是用delete而是一个名为getRidOfInvestment的函数:

    std::tr1::shared_ptr<Investment> createInvestment()
    {
        std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0), getRidOfInvestment());
        retVal = ...; //令retval 指向正确对象
        return retVal;
    }

    tr1::shared_ptr有一个特别的好的性质:它会自动使用它的“每个指针专属的删除器”,因而消除另一个潜在的客户错误:

    所谓的“cross-Dll problem”。对象在一个dll中被new,却在另一个dll内被delete。在许多平台上,这一类“跨dll的new/delete成对运用”会导致运行期错误。shared_ptr没有这个问题,因为它缺省的删除器是来自“shared_ptr诞生所在的那个Dll”的delete。例如Stock是派生自Investment的:

    std::tr1::shared_ptr<Investment> createInvestment()
    {
        return std::tr1::shared_ptr<Investment>(new Stock);
    }

    返回的那个tr1::shared_ptr可被传递给任何其他的DLLs,无需在意“cross-DLL problem”。这个指向Stock的tr1::shared_ptr会追踪记录“当Stock的引用次数变成0时该调用的那个DLL's delete”。

    1.“促进正确使用”的办法包括接口一致性, 与内置类型的行为兼容

    2.“防止误用”的方法包括,建立新类型限制类型上的操作束缚对象值、以及消除客户的资源管理责任

    3.tr1::shared_ptr支持定制型删除器(custom deleter)。这可防范DLL问题,可被用来自动解除互斥锁(mutexes)等等

  • 相关阅读:
    暑假学习笔记(一)——初识Neo4j和APICloud入门
    置信规则库学习记录——1
    博客整理——宣传文案
    博客整理——软件工程实践总结
    博客整理——Alpha版冲刺
    博客整理——事后诸葛亮
    面试被企业拒绝后还有必要再申请吗?
    MapReduce分布式算法
    水题系列二:PhoneNumbers
    水题系列一:Circle
  • 原文地址:https://www.cnblogs.com/lidan/p/2324147.html
Copyright © 2020-2023  润新知