文章目录
系列文章:
C++的35个技巧阅读笔记(一)
C++的35个技巧阅读笔记(二)
C++的35个技巧阅读笔记(三)
C++的35个技巧阅读笔记(四)
本次笔记为More Effective 35最后的
杂项部分
32.在未来时态下发展程序
- 1、如果某个class在设计时,绝不打算成为derived classes(派生类),那么就不应该只是在头文件的class上端摆一样注释就好,而是应该以C++语法来阻止派生(见条款26)。
- 2、如果要求其所有对象实体都必须于heap内产生,应该以条款27厉行这项约束。
- 3、请为每一个class处理assignment 和copy constructor动作,即使没有人使用,现在没有不意味着将来没有。
- 4、请努力让classes的操作符和函数拥有自然的语法和直观的语义。请和内建类型的行为保持一致:如有疑惑,请看ints有怎么样的表现。
- 5、请让你的classes容易被正确地使用,不容易被误用,请接收“客户会犯错”的事实,并设计你的classes有预防、侦测或更正的能力。
- 6、请努力写出可移植代码。
- 7、请设计你的代码,使“系统改变所带来的冲击”得以局部化。尽可能采用封装性质,尽可能让实现细目成为private。尽量避免设计出virtual base classes,因为这种classes必须被其每一个derived class初始化。
- 8、提供完整的classes——即使某些部分目前用不到。
- 9、设计你的接口,使有利于共同的操作行为,阻止共同的错误。
- 10、尽量使你的代码一般化(泛化),除非有不良的巨大后果。尽量用“未来式思维”去思考,它可增加你的代码重用性、加强其可维护性、使它更健壮,并促使在一个“改变实乃必然”的环境中有着优雅的改变。
33.将非尾端类(non-leaf classes)设计为抽象类(abstract classes)
允许通过指针进行“同型赋值”而阻止“异型赋值”的方法:
- 1、通过两个赋值操作符重载,一个是virtual,一个是正常版本。该方法可以实现但是不推荐使用。
- 2、让operator=成为Animal的private函数。
class Animal
{
private:
Animal& operatro=(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;
...
liz1 = liz2; //很好
Chicken chick1, chick2;
...
chick1 = chick2; //很好
Animal *pAnimal1 = &liz1;
Animal *pAnimal2 = &chick1;
...
*pAnimal1 = *pAnimal2; //错误,企图调用private Animal::operator=
不幸的是,Animal是一个具体类,而上述方法却使得Animal对象彼此间的赋值动作也不合法:
Animal animal1,animal2;
animal1 = animal2;
正确的做法是:消除“允许Animal对象相互赋值”的需要,而完成此事的最简单做法是让Animal成为一个抽象类,Animal就无法被实例化。如果当初设计Animal要相互赋值,则改一个新的抽象类AbstractAnimal。
class AbstractAnimal
{
protected:
AbstractAnimal& operator=(const AbstractAnimal& rhs);
public:
virtual ~AbstractAnimal() = 0; //它必须内含至少一个纯虚函数,一般让destructor成为纯虚函数
...
};
class Animal : public AbstractAnimal
{
public:
Animal& operatro=(const Animal& rhs);
...
};
class Lizard : public AbstractAnimal {
public:
Lizard& operator=(const Lizard& rhs);
...
};
class Chicken : public AbstractAnimal {
public:
Chicken& operator=(const Chicken& rhs);
...
};
将函数“声明为纯虚函数”并非暗示它没有实现码,而是意味着:
- 1、目前这个class是抽象的。
- 2、任何继承此class的具体类,都必须将纯虚函数重新声明为一个正常的虚函数(也就是说,不可以再令它 = 0)。
如果是程序库中的具体类你要继承,( 从类库的实体类派生一个新类
)有如下做法:
- 1、将具体派生自既存的(程序库中的)具体类,但需要注意本条款一开始所验证的assignment相关问题,并且小心条款3所描述的数组相关陷阱。
- 2、试着在程序库的继承体系中找到一个更高层的抽象类,其中你需要大部分功能,然后继承它。
- 3、以“你所希望继承的那个程序库类”来实现你自己的新类,例如你可以令程序库类的一个对象成为你的data member,然后在你的新类中重新实现该程序库类的接口。
- 4、使用类库的类,修改自己的程序增加非成员函数,不重写派生类。
34.如何在同一个程序中结合 C++ 和 C
- 1、name mangling(名称重整),C中你的函数名称不能重载。要压抑name mangling,必须使用C++的extern "C"指令:
extern "C" {
void drawLine(int x1, int y1);
void simulate(int iterations);
}
- 2、Static 的初始化,一般在main函数最开始端插入一个static对象初始化构造操作,在最尾端安插一个static 对象的析构。
- 3、动态内存分配,程序的C++部分使用new 和delete,程序的C部分使用malloc(及其变种)和free。
- 4、数据结构的兼容性,将两个语言间的“数据结构传递”限制于C所能了解的形式;C++structs如果内含非虚函数,倒是不受此限。
35.让自己习惯于标准C++语言
-
在ARM(Annotated C++ Reference Manual)出版后的这些年,C++最重要的几项改变如下:
1、增加了一些新的语言特性:RTTI、namespace、bool、关键词mutable 和explicit、enums最为重载函数之自变量所引发的类型晋升转换,以及在“class定义区内直接为整数型const static class members设定初值”的能力。
2、扩充了Templates的弹性:允许member template存在、接纳“明白指示template当场实例化”的标准语法、允许function templates接受“非类型自变量”、可用class templates作为其他template的自变量。
3、强化了异常处理机制:
4、修改了内存分配历程:加入operator new[]和operator delete[],内存为分配成功时候抛出异常,在内存失败时返回0;
5、增加了新的转型形式:static_cast,dynamic_cast,const_cast 和 reinterpret_cast.
6、语言规则更为优雅精炼:重新定义虚函数时,其返回类型不再一定得与原定义完全吻合。 -
STL标准程序库改变:
1、支持C标准函数库。
2、支持strings。
3、支持本地化。
4、支持I/O。
5、支持数值应用。复数等
6、支持广泛用途的container (容器)和 algorithm(算法)。
补充:auto_ptr实例代码
- auto_ptr实例代码1
template<class T>
class auto_ptr {
public:
explict auto_ptr(T *p = 0);
template<class U>
auto_ptr(auto_ptr<U>& rhs);
~auto_ptr();
template<class U>
auto_ptr<T>& operator=(auto_ptr<U>& rhs);
T& operator*() const;
T* operator->() const;
T* get() const;
T* release();
void reset(T *p = 0);
private:
T *pointee;
template<class U>
friend class auto_ptr<U>;
};
template<class T>
template<class U>
inline auto_ptr<T>::auto_ptr(auto_ptr<U>& rhs) : pointee(rhs.release()) {}
template<class T>
inline auto_ptr<T>::~auto_ptr() { delete pointee; }
template<class T>
template<class U>
inline auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs)
{
if(this != &rhs) reset(rhs.release());
return *this;
}
template<class T>
inline T& auto_ptr<T>::operator*() const { return pointee; }
template<class T>
inline T* auto_ptr<T>::operator->() const { return pointee; }
template<class T>
inline T* auto_ptr<T>::get() const { return pointee; }
template<class T>
inline T* auto_ptr<T>::release()
{
T* oldPointee = pointee;
pointee = 0;
return oldPointee;
}
template<class T>
inline void auto_ptr<T>::reset(T*p)
{
if(pointee != p) {
delete pointee;
pointee = p;
}
}
- auto_ptr实例代码2
//所有函数都定义在class定义区内:
template<class T>
class auto_ptr {
public:
explicit auto_ptr(T *p = 0) : pointee(p) {}
template<class U>
auto_ptr(auto_ptr<U>& rhs) : pointee(rhs.release()) { }
~auto_ptr() { delete pointee: }
template<class U>
auto_ptr<T>& operator=(auto_ptr<U>& rhs)
{
if(this != &rhs) reset(rhs.release());
return *this;
}
T& operator*() const { return *pointee; }
T* operator->()const { return pointee; }
T* get() cosnt { return pointee; }
T* release()
{
T *oldPointee = pointee;
pointee = 0;
return oldPointee;
}
void reset(T *p = 0)
{
if(pointee != p) {
delete pointee;
pointee = p;
}
}
private:
T* pointee;
template<class U> friend class auto_ptr<U>;
};