//条款26:尽量延后变量的定义式出现的时间
// 1.不仅应该延后变量的定义,更应该直到使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初始值为止。如果这样,不仅能够避免构造和析构的非必要操作,还可以避免毫无意义的调用默认构造函数带来的开销。 //条款27:尽量少做转型动作
// 1.尽量以C++风格的转型替代C风格的转型,前者更加安全。
// 2.const_cast 通常用来将对象的常量性转除(cast away the constness)。它是唯一有此能力的C++-style转型操作符。
// 3.dynamic_cast用来执行继承体系中安全的向下转型或跨系转型动作。也就是说你可以利用它将指向基类对象的指针或者引用转型为指向派生类对象的指针或引用,并得知转型是否成功。如果转型失败,
// 会以一个null指针(当转型对象是指针)或一个exception(当转型对象是引用)表现出来。dynamic_cast是唯一无法由旧式语法执行的转型动作,也是唯一可能消耗重大运行成本的转型动作。
// 4.static_cast 基本上拥有与C旧式转型相同的威力与意义,以及相同的限制。例如将一个非 const 的对象转换为 const 对象,或将int 转换为 double等等。它也可以用来执行上述多种转换的反向转换,
// 例如将void*指针转为typed指针,将pointer-to-base转为pointer-to-derived。但是他无法将const转为non-const,这个只有const-cast才能够办到。
// 5.reinterpret_cast意图执行低级转型,实际动作及结果可能取决于编译器,这也就表示它不可移植。例如将一个pointer to int 转型为int。这一类转型在低级代码以外很少见
//dynamic 英 [daɪˈnæmɪk] 美 [daiˈnæmik] adj.动态的
//reinterpret 英 [ˌri:ɪnˈtɜ:prɪt] 美 [ˌri:ɪnˈtɜ:rprɪt] vt. 重新解释
class CFather
{
public:
CFather(){}
virtual ~CFather(){} virtual void Fun(){printf("Father\n");}
};
class CChild : public CFather
{
public:
CChild() : nValue(-1){} public:
virtual void Fun(){printf("Child\n");} void FunTest()
{
char buff[10] = {};
sprintf_s(buff, "%d\n", nValue);
printf(buff);
} public:
int nValue;
}; int main()
{
{
CChild Child;
CFather *pFather = dynamic_cast<CFather*>(&Child);
pFather->Fun(); //输出Child CFather Father;
CChild *pChild = dynamic_cast<CChild*>(&Father); //pChild = 0x00000000
}
{
CChild Child;
CFather *pFather = static_cast<CFather*>(&Child);
pFather->Fun(); //输出Child
}
{
CFather Father;
CChild *pChild = static_cast<CChild*>(&Father); //pChild = 0x0036fd04
pChild->Fun(); //输出Father
pChild->FunTest(); //运行奔溃
} {
const int value = 10;
int *value1 = const_cast<int*>(&value);
//int *value1 = static_cast<int*>(&value); //无法从“const int *”转换为“int *”
*value1 = 1; //value = 1 value1 = 0x0026fe48 int value2 = reinterpret_cast<int>(value1); //value2 = 0x0026fe48
//int value2 = static_cast<int>(value1); //error C2440: “static_cast”: 无法从“int *”转换为“int”
}
} //条款28:避免返回一个handle指向对象的内部成分
// 1.当类定义了一个函数来返回其内部数据的一个指针或者引用的时候,此时会破坏数据的封装性
class CTest
{
public:
CTest(){pValue = new int(0);}
~CTest(){delete pValue;}
int& GetValue() const {return *pValue;} //即使此函数是const的,也不能改变数据封装性被破话的事实 private:
int *pValue;
};
CTest Test;
Test.GetValue() = 10; //此举就是破话了数据的封装性
int value = Test.GetValue(); //value = 10
// 2.当将上述GetValue函数的返回类型也用const修饰,那么就可以避免带来数据封装性的破话,但是,若pValue指向的内存被释放,那么就将带来指针空悬问题。
// 3.有时候必须返回类的成员数据的引用,比如vector中的[]操作。但是使用的时候也必须小心。 //条款30:透彻了解inline的里里外外
// 1.将大多数inline限制在小型的、被频繁调用的函数身上,可以使代码膨胀问题最小化,使程序速度提升最大化。
// 2.inline函数是编译时期的,可能会对inline函数的调试产生问题。
// 3.不能滥用inline,滥用inline可能会导致程序体积过大,也可能导致额外的换页行为,降低指令高速缓冲装置的击中率,以及伴随而来的效率问题。 //条款31:将文件间的编译依存关系降至最低
// 1.若在类A中定义了B类对象,那么类A所在的头文件一般都需要#include "B.h",那么当B类改变后,类A所在的头文件也必须重新编译。
// 2.针对上述情况,若类A定义的是B类的指针对象,则只需要使用前置说明而不需要在类A所在的头文件中#include "B.h",那么就不需要在B类改变后,重新编译类A所在的头文件。
// 3.综上可知,以声明的依存性替代定义的依存性,那正是编译依存性最小化的本质。现实中,让头文件尽量自给自足,万一做不到,则让它与其他文件内的声明式相依。
// 4.综上可知,尽量不要在头文件中#include其他头文件,从而避免产生依赖性编译。尽量在头文件中使用前向声明,而在cpp文件中使用#include
// 5.在头文件中,要以引用或者指针替代对象。
// 6.将类的声明和定义分别置于两个不同的文件,以降低文件间的编译依存关系。