Constructors, Destructors, operator=
Item5:四大默认
C++编译器默认提供了三个构造函数和一个析构函数
class Empty {
public:
Empty() { ... } // default constructor
Empty(const Empty& rhs) { ... } // copy constructor
~Empty() { ... } // destructor
Empty& operator=(const Empty& rhs) { ... } // copy assignment operator
};
Item6:拒绝复制
boost库有个noncopyable类,类似下面的类,作为想要拒绝复制的类的基类
class Uncopyable {
protected: // allow construction
Uncopyable() {} // and destruction of
~Uncopyable() {} // derived objects...
private:
Uncopyable(const Uncopyable&); // ...but prevent copying
Uncopyable& operator=(const Uncopyable&);
};
Item7:为多态基类声明virtual析构函数
polymorphic base classes实如其名,目的为了子类对象在面向接口编程时的正确释放。
Item8:别让异常逃离析构函数
新规范析构函数默认noexcept(true),若抛出异常,就直接terminate。书里主要是说不明确行为,有俩异常时也会terminate。
class DBConn {
public:
...
void close() // new function for client use
{
db.close();
closed = true;
}
~DBConn()
{
if (!closed) {
try { // close the connection
db.close(); // if the client didn’t
}
catch (...) { // if closing fails, note that and terminate or swallow
make log entry that call to close failed;
...
}
}
}
private:
DBConnection db;
bool closed;
};
Item9:绝不在构造和析构过程中调用virtual函数
由于构造函数和析构函数调用顺序,所以在基类构造和析构时,虚构函数并不会下降到子类(既然虚构理论上就是想让其为基类)。
During base class construction, virtual functions never go down into derived classes.
Item10:令operator= 返回一个reference to *this
保持正常逻辑可行,是好习惯。
Item11:在operator= 里处理“自我赋值”
在自我赋值的道路上要考虑异常安全,当然copy and swap 和 pass by value更简洁。(C++ primer里也有讲)
Widget& Widget::operator=(const Widget& rhs) // copy and swap
{
Widget temp(rhs);
swap(temp);
return *this;
}
Widget& Widget::operator=(Widget rhs) // pass by value
{
swap(rhs);
return *this;
}
Item12:复制对象时勿忘其每一个成分
实现一个copying function(如拷贝或赋值构造函数)时,
(1) copy all local data members and
(2) invoke the appropriate copying function in all base classes, too. //调用基类的copying函数
不要尝试去实现一个copying函数调用另一个,应该把共同机能放到第三个函数中然后去调用(init函数)。
Resource Management
Item13:以对象管理资源
RAII(Resource Acquisition Is Initialization)
智能指针(C++11已全部成了C++特性了)
auto_ptr shared_ptr 以及boost库的 boost::scoped_array boost::shared_array
Item14:在资源管理类中小心coping行为
这是上一条款的后续,常见行为:
禁止复制(如muduo网络库里的MutexLockGuard)
引用计数(shared_ptr)
深度拷贝
转移资源拥有权(auto_ptr)
class Lock {
public:
explicit Lock(Mutex *pm)
: mutexPtr(pm, unlock) // init shared_ptr with the Mutex to point to and the unlock func as the deleter
{
lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr;
};
Item15:在资源管理类中提供对原始资源的访问
用户可能需要原始资源作为参数传入某个接口, 如:
class Font {
public:
explicit Font(FontHandle fh)
: f(fh)
{}
~Font( ) { releaseFont(f ); }
...
private:
FontHandle f;
};
想要与下面的API通讯:
void changeFontSize(FontHandle f, int newSize);
一、提供显示调用接口:
FontHandle get() const { return f; }
二、提供隐式转换接口(参考stackoverflow):
operator FontHandle() const { return f; }
如同作者推荐单参数构造函数使用explicit禁止隐式转换一样,作者更倾向于使用显示调用接口。
Item16:成对使用new和delete时要采用相同的形式
std::string *stringPtr1 = new std::string;
std::string *stringPtr2 = new std::string[100];
...
delete stringPtr1; // delete an object
delete [] stringPtr2; // delete an array of objects
平时使用倒是不会搞错,有一点倒是容易被误会
typedef std::string AddressLines[4];
std::string *pal = new AddressLines;
delete [] pal; // fine
Item17:以独立的语句将newd对象置入智能指针
下面由于执行顺序,可能会导致内存泄漏
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
应该像下面的样子写
std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());
Designs and Declarations
Item18:让接口容易被正确使用,不易被误用
面向用户设计API,限制API使用的可能性(使传参、返回值变窄)。
Item19:设计class犹如设计type
主要讲了设计一个类时需要考虑的事情(纯理论版,用到再去对照)。
Item20:以pass-by-refrence-to-const替换pass-by-value
除了内置类型、STL迭代器和函数对象。都应用pass-by-reference-to-const方式传值。
Item21:必须返回对象时,别妄想返回其reference
没啥说的,基本问题了(作者写的真多…)。
Item22:将成员变量申明为private
protected并不比public更有封装性(这条又是基本原则了,又被作者写了好多…)
Item23:宁以non-member,non-friend替换member
member函数越多越破坏封装性。在能不定义member函数也可完成功能时,选择non-member,non-friend更好。后者增加封装性、包裹弹性和机能扩充性。
Item24:若所有参数都需要类型转换,请为此采用non-member函数
譬如operator * 操作符重载时涉及了隐式类型转换。
Item25:考虑写一个不抛出异常的swap函数
std提供了swap,其实现方式如下:
//namespace std
template< typename T >
void swap(T& a, T& b)
{
T temp(a); a=b;
b = temp;
}
拷贝多次,可能存在效率问题,所以有时候需要自己实现:
class Widget {
public:
...
void swap(Widget& other)
{
using std::swap; //很重要
swap(pImpl, other.pImpl);
}
...
};
在类外模板特例化(C++ Primer pg624)
//namespace std
template<>
void swap(Widget& a, Widget& b)
{
a.swap(b);
}
当然,也可以直接类外写函数模板调用swap成员函数。
总结
回头来看,第二章在阐述如何写好每一个构造函数和析构函数。
正如第三章所说应遵从RAII方式以对象管理资源,我时常会头疼异常了怎么办,第二章的不让异常逃离析构函数让我豁然开朗。
第四章似乎围绕着封装来阐述,除了最后一条,其他看似不新鲜却非常有设计感。