条款18:让接口容易被正确使用,不易被误用
shared_ptr的一个特性:不会出现”corss-DLL problem“,即对象在动态链接库(DLL)中被new创建,却在另一个DLL内被delete销毁,因为它会自动使用”每个指针专属的删除器“,当在其他DLL中时会自动追踪记录确定初始调用的那个DLL‘s delete
条款19:设计class犹如设计type
条款20:宁以pass-by-reference-to-const替换pass-by-value
pass-by-value首先会极大浪费空间及时间效率,参数都是以实参的副本为初值,调用端获得的是函数返回值的一个复件,这些复件由copy构造函数产出,且当遇到继承情况时还会出现slicing(对象切割)问题,即当derived class对象以pass-by-value传递并被视为一个base class对象,base class的copy构造函数会被调用,而derived class中的某些特性会被切割
class A{
public:
virtual void func();
};
class B:public A
{
public:
virtual void func();
};
void F(A a)
{
a.func();
}
B b;
F(b);//pass-by-value,b调用的永远都是A::func()
内置类型对象(int等)还是推荐使用pass-by-value
条款21:必须返回对象时,别妄想返回其reference
条款22:将成员变量声明为private
条款23:宁以non-member, non-friend 替换member函数
class WebBrowser
{
public:
void clearCache();
void clearHistory();
void clearBookmarks();
void clearCookies();
}
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.clearBookmarks();
wb.clearCookies();
}上述
条款24:若所有参数皆需类型转换,请为此采用non-member函数
条款25:考虑写出一个不抛异常的swap函数
pimpl手法(pointer to implementation):以指针指向一个对象,内含真正的数据
class WidgetImpl
{
public:
...
private:
int a,b,c;
vector<double> v;
}
class Widget
{
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs)
{
...
*pImpl = *(rhs.pImpl);
...
}
private:
WidgetImpl* pImpl;
}
条款26:尽可能延后变量定义式的出现时间
string s;
if(....)
{
....
}
....
return s;
条款27:尽量少做转型动作
1.const_cast
将对象的常量性转除
2.dynamic_cast
多用于继承对象之间
3.reinterpret_cast
执行低级转换,实际结果取决于编译器,因此不可移植
4.static_cast
class Base{...};
class Derived : public Base
{...}
Derived d;
Base* pb = &d;
pb和d的指针值并不相同,这时会有一个偏移量在运行期间被施行于Derived*指针身上,用以取得正确的Base*指针值。
条款28:避免返回handles指向对象内部成分
class Point
{
public:
Point(int x, int y);
void setX(int newVal);
void setY(int newVal);
};
struct RectData
{
Point ulhc;
Point lrhc;
};
class Rectangle
{
public:
Point& upperLeft() const { return pData->ulhc; }
Point& lowerRight() const {return pData->lrhc;}
private:
shared_ptr<RectData> pData;
}
上述代码可通过编译,但实际上却是错的,自我矛盾。upperLeft和lowerRight为const函数 ,返回的是reference,调用者可以通过这些reference更改内部数据。
条款29:为异常安全而努力是值得的
条款30:透彻了解inlining的里里外外
条款31:将文件间的编译依存关系降到最低
int x;
Person p(params);
当编译器看到时,必须知道要分配多少内存来维持x和p,特别是p,编译器需要知道需要分配多少内存来放置一个Person对象,那么唯一方法就是询问class Person的定义式
class PersonImpl;
class Date;
class Address;
class Person
{
public:
...
private:
//将对象实现细目隐藏在指针后
shared_ptr<PersonImpl> p;
};
这样一来,Person就与Date、Address的实现细目分离了,这种分离的关键在于以”声明的依存性“替换”定义的依存性“。