More Effective C++ 第六部分 杂项讨论

来源:互联网 发布:e领网络卡盟 编辑:程序博客网 时间:2024/06/05 18:38

32.在未来时态下发展程序

软件最初的开发与后续开发和维护的人通常不是同一批,所有我们需要强制某些规范,例如利用c++语言特性强制对象产生与heap内。不要想着我记得不去做某些事就行,需要强制实行。

确保operator和函数拥有自然的语意。应和内建类型一样,如果疑惑,不妨看看ints有怎样的表现。

任何事情之一有人能做,就会有人做。例如抛出异常,将对象自我赋值,在为获得初值前使用对象,给函数非法的值……

尽量避免使用RTTI作为设计基础的层层的if else语句。

提供完备的类,即使某些部分现在还没有被使用。如果有了新的需求,你不用回过头去改它们。
将你的接口设计得便于常见操作并防止常见错误。使得类容易正确使用而不易用错。例如,阻止拷贝构造和赋值操作,如果它们对这个类没有意义的话。防止部分赋值。

尽量使你的代码泛化,除非有巨大的不良后果。例如,如果在写树的遍历算法,考虑将它通用得可以处理任何有向不循环图。 未来时态的考虑增加了你的代码的可重用性、可维护性、健壮性,已及在环境发生改变时易于修改。它必须与进行时态的约束条件进行取舍。


33.将非尾端类设计为抽象类

在原有具体类被当做基类使用时,需要到导入一个新的抽象类。

这里写图片描述
其中C1是一个具体类,是可以当做正常的对象被构造出来并有意义的,A是一个抽象类,其含有C1,C2的共同特性。

若不采用这种方法导致的问题的一个例子:
这里写图片描述

class Animal {public:    Animal& operator=(const Animal& rhs);    ...};class Lizard: public Animal {public:    Lizard& operator=(const Lizard& rhs);    ...};class Chicken: public Animal {public:    Chicken& operator=(const Chicken& rhs);    ...};

下面这样的代码将会出现问题

Lizard liz1,liz2;Animal* pAnimal1 = &liz1;Animal* pAnimal2 = &liz2;*pAnimal1 = *pAnimal2;//将会调用Animal的operator=,导致liz1和liz2的base class部分相同,//但derived class部分还是原来的

一个解决办法是让operator=成为虚函数,接受一个Animal&类型参数,返回对应的类型。(C++要求虚函数原型相同,尽管允许函数返回引用时可以返回派生类引用,但函数参数仍然必须完全相同.)例如

class Lizard: public Animal {public:    virtual Lizard& operator=(const Animal& rhs);    ...};

但是这样将导致将一只鸡赋值给一只蜥蜴是“正确”的,因为其接受对象的类型是Animal&。
解决这个问题的方式使用dynamic_cast但会导致性能和异常问题;

class Lizard: public Animal {public:    virtual Lizard& operator=(const Animal& rhs);    Lizard& operator=(const Lizard& rhs); //同类型的不需要dynamic_cast    ...};Lizard& Lizard::operator=(const Animal& rhs){    return operator=(dynamic_cast<const Lizard&>(rhs));}

我们不能阻止Animal对象间的赋值,因为其位具体类,是可以赋值的。

终极解决方案是:
这里写图片描述

采用这种策略直接禁止了像*pAbstractAnimal1=*pAbstractAnimal2的操作,而仍然像*pAnimal1=*pAnimal2的操作并且在编译时检查类型.

然而有时候需要使用第三方库,并继承其中一个具体类,由于无法修改该库,也就无法将该具体类转为抽象基类,这是就需要采取其他选择:
1). 继承自现有的具体类,但要注意上边的assignment问题,并小心条款3所提出的数组陷阱.
2). 试着在继承体系中找一个更高层的抽象类,然后继承它.
3). 以”所希望继承的那么程序库类”来实现新类.例如使用复合或private继承并提供相应接口.此策略不具灵活性.
4). 为”所希望继承的那么程序库类”定义一些non-member,不再定义新类.


34.如何在同一程序中结合C++和C

压抑name mangling

c++的函数经过name mangling产生独一无二的名称,而c并不会经过name mangling,因为c不支持重载。所以在C++程序中调用c程序需要压抑name mangling。方法如下:

extern "C"      //使用extern "C"  压抑void drawLine();//C函数extern "C"{    //一些C函数}

当某些头文件由C和C++共有可以这样写

#ifdef _cplusplusextern "c"{#endif//函数声明#ifdef _cplusplus}#endif

static的初始化

static对象在main开始前构造,在mian结束后析构,与C冲突。为解决这一问题每个编译器厂商都会提供某种语言以外的机制启动static对象的构造和析构,具体方法需要查看编译器文档。

动态内存分配

C++使用new和delete,C使用malloc和free,需要一一对应,不能用new分配内存用free释放。

数据结构的兼容性

C和C++之间对数据结构做双向交流应该是安全的。因为Structor是兼容的,除非其中包含虚函数。


35.让自己习惯与标准C++语言

……

0 0