More Effective C++ 第六部分 杂项讨论
2015-12-18 19:11
316 查看
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++语言
……相关文章推荐
- c++读取文件内容并保存到二维数组
- 用OC语言实现贪吃蛇小游戏
- C++ thrift详细教程 及和Protobuf对比
- C语言结构体 别名定义
- C语言 gets()和scanf()函数的区别
- C++11: regex #1
- 【转载】C语言中scanf格式化输入函…
- C++中的向量学习
- C++中的set
- [转]C++map的基本操作和使用
- C /C++标准库 - <ctime> (time.h)
- 黑马程序员—C语言—(关键字、标识符、数据、常量、变量、注释)
- C++处理异常技巧-try,catch,throw,finally
- HDOJ&nbsp;&nbsp;2024&nbsp;&nbsp;&nbsp;&nbsp;C语言合法标识符
- C语言中的qsort函数
- C++中的sort函数
- 黑马程序员-C语言-Xcode常用快捷键
- C++学习【原创】Orders(nex…
- 黑马程序员 - C语言 -分支、循环
- 注释转换