通过default构造函数出一个对象再对他赋值比直接在构造时指定赋值 差。
比如
string str("honey~");和
string str; str="honey~";效率就不一样。
接下来讲循环时的初始化,
/*
class A;
A a;
for(int i=0;i<n;i++) //1次构造,1次析构,n次赋值。
{a=**;}
*/
class A;
for(int i=0;i<n;i++) //n次构造,n次析构
{
A a(**);
}
//综上,下面的情况好。
转型分为旧式转型和新式转型。
旧式转型: (T) expression
新式转型:
1.const_cast<T>(expression) cast away the constness
将对象的常量性移除
2.dynamic_cast<T>(expression) safe downcasting
唯一不能用旧式转型实现的转型动作。不过可能耗费重大运行成本的动作。
3.reinterpret_cast <T>(expression) 低级转型
例:int*->int
4.static_cast<T>(expression) 强迫隐式转换。
例:non-const -> const ,int->double
pointer-to-base ->pointer-to-derived
void* -> typed 指针
现在运用旧式转型符的时机是调用explicit构造函数。
handle:reference,指针,迭代器。
函数不要返回一个指向访问级别较低的成员函数。因为会降低封装性。
成语函数返回对象内部的handle可能会使dangling。
下面来看异常:
异常安全函数保证:
1.基本承诺:异常不出错即可能出于任何合法状态。
2.强烈保证:异常返回前一状态。
3.不投掷保证:不跑出异常。
如int f() throw(); //不是说不抛出异常,而是只要抛出异常,就导致严重错误。
动态分配不成功就 bad_alloc异常。
如果一个系统中有一个函数不具备异常安全性,则整个系统不具备异常安全性。
封装,异常安全性。
inline函数
1.免除调用成本
2.如果函数体过大,则会换页行为和降低指令高速缓存装置的击中率。
3.对比函数本体和函数调用所产生的代码长度。
4.因为inlining是编译期行为,所以要在头文件中。
5.template通常也 是inline但不全是。
//此实例为了证明构造函数和析构函数最好不要inline class Base { public: ... private: string bm1,bm2; }; class Derived { public: Derived(){} //看起来是空的。 private: stirng dm1,dm2,dm3; }; Derived::Derived() { Base::Base(); try{dm1.string::string();} catch(...) { Base::~Base(); throw; } try{dm2.string::string();} catch(...) { dm1.string::~string(); Base::~Base(); throw; } try{dm3.std::string::string();} catch(...) { dm2.string::~string(); dm1.string::~string(); Base::~Base(); throw; } }
pimpl idiom 将实现放入一个类,在接口类中定义一个智能指针指向实现类,从而达到与实现细目分离。