前言
“学习程序语言根本大法是一回事;学习如何以某种语言设计并高效程序则是另一回事。”这段话来自《Effective C++》的导读中。告诉了我们学会使用语言和灵活高效使用它们是同样的重要。
上一周拜读了这一本经典的名作,发现这本书中对C++编程中的一些设计模式和安全性(主要是针对C++中的OOP)里有许多非常有意思的探讨,我在这里记录一下方便以后查阅。
设计模式
条款4:确定对象被使用前以先被初始化。(单例模式)
c++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。
如果在多线程的情况下,就会有饥汉模式和包含模式的探讨了。条款13:以对象管理资源(工厂模式)
获得资源后立刻放进管理对象
管理对象运用析构函数确保资源被释放- 让接口容易被正确使用不易被误用
如果我们要实现createInvestment使它返回一个tr1::shared_ptr并带getRidOfInvestment函数作为删除器,代码:
std::tr1::shared_ptr<Investment> createInvestment()
{
std::tr1::shared_ptr<Investment> retVal(static_cast<Investment* >(0),getRidOfInvestment);
retVal = ...; //令retVal指向正确的对象
return retVal;
}
- 条款31:将文件间的编译依存关系降至最低(策略模式)
#include "Person.h" //我们正在实现Person class
//所以必须#include其class定义式
#include "PersonImpl.h" //我们也必须#include "PersonImpl.h"的
//class定义式,否则无法调用其成员函数
//注意,PersonImpl有着和Person
//完全相同的成员函数,两者的接口完全相同。
Person::Person(const std::string& name, const Date& birthday,const Address& addr)
:pImpl(new PersonImpl(name,birthday,addr))
{}
std::string Person::name() const
{
return pImpl->name();
}
Person构造函数以new调用PersonImpl构造函数,以及Person::name函数内调用PersonImpl::name。这是重要的,让Person变成一个Handle class并不会改变它做的事,只会改变它做事的方法。
在程序发展过程中使用Handle classes 和 Interface classes 已求实现码有所变化时对其客户带来最小冲击。
- 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handle classes 和 Interface classes。
- 程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及templates都适用。
安全性
关于程序的安全性,我就挑几点我认为比较重要的讲一讲把,毕竟这个问题实在是太宽泛了,若以后我的技术又有长进我就再来填坑把。
尽量以const,enum,inline替换 #define
#define不视为语言的一部分,因而编译器不会处理#define定义的语句为多态基类声明virtual声明析构函数
每个函数负责析构自己在堆上申请的内存部分,先析构子类再析构基类。绝不在构造和析构过程中调用virtual函数
在derived class对象的base class构造期间,对象的类型是base class而不是derived class
在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class。以对象管理资源
为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。考虑写出一个不抛出异常的swap函数
namespace std{
template<typename T> //std::swap的典型实现
void swap(T& a, T& b) //置换a和b的值
{
T temp(a);
a = b;
b = temp;
}
}
- 当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
如果你提供一个member swap,也该提供一个non-member swap来调用前者。对于class(而非templates),也请特化std::swap。
条款29:为“异常安全”而努力是值得的
- 异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏。这样的函数三种保证:基本型,强烈型、不抛异常型
- “强烈保证”往往能够以copy-and-swap实现出来,原则:为你打算修改的对象(原件)做出一份副本,然后再在那副本上做一切必要的修改。若有任何修改动作抛出异常,原对象仍保持未改变状态。待所有改变都成功后,再将修改过的那个副本和原对象在一个不抛出异常的操作中置换(swap)。