美食家大橙子 发表于 2024-5-19 00:23:10

《Effective C++》第三版-4. 设计与声明(Design and Declarations)

目次

[*]条款17:让接口容易被精确使用,不易被误用(Make interfaces easy to use correctly and hard to use incorrectly)

[*]限制类型和值
[*]规定能做和不能做的事
[*]提供举动一致的接口

[*]条款19:设计class犹如设计type(Treat class design as type design)
[*]条款20:宁以pass-by-reference-to-const更换pass-by-value(Prefer pass-by-reference-to-cons to pass-by-value)

[*]避免构造和析构
[*]避免对象切割
[*]破例

[*]条款21:必须返回对象时,别妄想返回其reference(Don’t try to return a reference when you must return an object)
[*]条款22:成员变量声明为private(Declare data members private)
[*]条款23:宁以non-member、non-friend更换member函数(Prefer non-member non-friend functions to member functions)
[*]条款24:若所有参数皆需类型转换,请为此采用non-member函数(Declare non-member functions when type conversions should apply to all parameters)
[*]条款25:思量写出一个不抛非常的swap函数(Consider support for a non-throwing swap)

[*]缺省的swap
[*]特化的swap
[*]使用swap的总结


条款17:让接口容易被精确使用,不易被误用(Make interfaces easy to use correctly and hard to use incorrectly)

限制类型和值

class Date {
public:
        Date(int month, int day, int year);//可能月日年顺序错,可能传递无效的月份或日期
        ...
};可使用类型系统(type system)规避以上错误,即引入外覆类型(wrapper type)区别年代日:
struct Day {
explicite Day(int d)
        : val(d) { }
int val;
}
struct Month {
explicite Month(int m)
        : val(m) { }
int val;
}
struct Year{
explicite Year(int y)
        : val(y) { }
int val;
}

class Date {
public:
        Date(const Month& m, const Day& d, const Year& y);//可能月日年顺序错,可能传递无效的月份或日期
        ...
};
Date d(Month(3), Day(30), Year(1995));//可有效防止接口误用保证了类型精确之后,需要保证输入的值有用:
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);
        ...
};
Date d(Month::Mar(), Day(30), Year(1995));规定能做和不能做的事

if ( a * b = c) ...//以const修饰操作符*,使其不能被赋值提供举动一致的接口

为了避免忘记删除或者重复删除指针,可令工厂函数直接返回智能指针:
Investment* createInvestment(); //用户可能忘记删除或者重复删除指针
std::tr1::shared_ptr<Investment> createInvestment();若盼望用自定义的getRidOfInvestment,则需要避免误用delete,可思量将getRidOfInvestment绑定为删除器(deleter):
删除器在引用次数为0时调用,故可创建一个null shared_ptr
std::tr1::shared_ptr<Investment> createInvestment()
{
        std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0),
                                                                                                                                                                        getRidOfInvestment);//创建一个null shared_ptr
        retVal = ... ;//令retVal指向目标对象
        return retVal;
}若pInv管理的原始指针能在pInv创立之前确定下来,则将原始指针直接传递给pInv的构造函数更好
tr1::shared_ptr会主动使用每个指针专属的删除器,从而无须担心cross-DLL problem:
cross-DLL problem:对象在动态毗连程序库(DLL)中被new创建,但在另一个DLL内被delete销毁
//返回的tr1::shared_ptr可能被传递给任何其他DLL
//其会追踪记录从而在引用次数为0时调用那个DLL的delete
std::tr1:;shared_ptr<Investment> createInvestment()
{
        return std::tr1::shared_ptr<Investment>(new Stock);
}Boost的tr1::shared_ptr特点:

[*]是原始指针的两倍大
[*]以动态分配内存作为簿记用途和删除器的专属数据
[*]以virtual形式调用删除器
[*]在多线程程序修改引用次数时有线程同步化(thread synchronization)的额外开销
Tips:

[*]好的接口不易被误用
[*]促进精确使用的方法包括接口一致性和与内置类型的举动兼容
[*]阻止误用的办法包括建立新类型、限制类型上的操纵、束缚对象值、消除客户的资源管理责任
[*]tr1::shared_ptr支持定制型删除器(custom deleter),这可以防范DLL题目,可被用来主动解除互斥锁(mutexes)等
条款19:设计class犹如设计type(Treat class design as type design)

定义一个新class时也就定义了一个新type。设计高效的类需要思量以下题目:

[*]新type的对象应如何创建和销毁(第8章))

[*]影响构造函数和析构函数、内存分配函数和开释函数(operator new,operator new [],operator delete,operator delete [])

[*]对象的初始化和赋值应有什么差别(条款4)

[*]决定构造函数和赋值操纵符的举动

[*]新type的对象如果被pass-by-value意味着什么

[*]由copy构造函数定义pass-by-value如何实现

[*]什么是新type的合法值

[*]有用的数值集决定了类必须维护的约束条件(invariants),

[*]进而决定了成员函数(特别是构造函数、析构函数、setter函数)的错误查抄

[*]还影响函数抛出的非常和少少使用的函数非常明细列(exception specifications)

[*]新type需要共同某个继承图系(inheritance graph)吗

[*]继承既有的类,则受那些类束缚,尤其要思量那些类的函数是否为虚函数
[*]被其他类继承,则影响析构函数等是否为virtual

[*]新type需要什么样的转换

[*]若允许类型T1隐式转换为类型T2,可可思量:

[*]在T1类内写类型转换函数(operator T2)
[*]在T2类内些non-explicit-one-argument(可被单一实参调用)的构造函数
[*]若只允许explicit构造函数存在,就得写专门执行转换的函数,且没有类型转换操纵符(type conversion operators)或non-explicit-one-argument构造函数


[*]什么样的操纵符和函数对于此新type合理

[*]决定需要声明哪些函数,其中哪些是成员函数

[*]什么样的标准函数应驳回

[*]这些必须声明为private

[*]谁改取用新type的成员

[*]影响public、private、protected的选择
[*]影响友元类、友元函数、及其嵌套的设计

[*]什么是新type的未声明接口(undeclared interface)

[*]要思量其对效率、非常安全性、资源运用的保证

[*]新type有多么一般化

[*]若要定义整个type家族,则应该定义新的class template

[*]是否真的需要新type

[*]若定义新的派生类就足够,则大概定义non-member函数或templates更好

Tips:

[*]Class设计就是type设计,需要思量以上所有题目
条款20:宁以pass-by-reference-to-const更换pass-by-value(Prefer pass-by-reference-to-cons to pass-by-value)

避免构造和析构

class Person {
public:
        Person();
        virtual ~Person();
        ...
private:
        std::string name;
        std::string address;
};
class Student: public Person {
public:
        Student();
        ~Student();
        ...
private:
        std::string schoolName;
        std::string schoolAddress;
};
bool validateStudent(Student s);//会调用六次构造函数和六次析构函数
bool validateStudent(const Student& s);//效率提升很多上述代码validateStudent函数中pass-by-value会调用六次构造函数和六次析构函数:

[*]Student构造+Person构造+Student的2个string+Person的2个string
[*]析构同理
使用pass-by-reference可避免频繁构造和析构
避免对象切割

对象切割(slicing):派生类以值传递并被视为基类对象时,回调用基类的构造函数,而派生类的成分全无
class Window {public:        ...        std::string name() const;//返回窗口名称        virtual void display() const;//显示窗口和其内容};class WindowWithScrollBars: public Window {public:        ...        virtual void display() const;};void printNameAndDisply(Window w){        std::cout
页: [1]
查看完整版本: 《Effective C++》第三版-4. 设计与声明(Design and Declarations)