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

Posted zhangqixiang5449

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了More Effective C++ 第六部分 杂项讨论相关的知识,希望对你有一定的参考价值。

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 _cplusplus
extern "c"
#endif

//函数声明

#ifdef _cplusplus

#endif

static的初始化

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

动态内存分配

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

数据结构的兼容性

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


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

……

以上是关于More Effective C++ 第六部分 杂项讨论的主要内容,如果未能解决你的问题,请参考以下文章

More Effective C++ 第三部分 异常

《More Effective C++》总结笔记

More Effective C++ 第四部分 效率

《More Effective C++》总结笔记——异常

《More Effective C++》阅读笔记

More Effective C++ 第一部分 基础议题