《More Exceptional C++》------ ( 3 )
2010年9月03日 22:42 | Comments(1) | Category:Reading Notes | Tags:
class CFuncPtrHelp; typedef CFuncPtrHelp ( *FuncPtr ) ( void ); class CFuncPtrHelp { public: CFuncPtrHelp( FuncPtr p ) : m_pFunc( p ) {} operator FuncPtr() { return m_pFunc; } private: FuncPtr m_pFunc; }; CFuncPtrHelp Func() { //other useful operation return Func; } int _tmain(int argc, _TCHAR* argv[]) { CFuncPtrHelp ptr = Func(); ptr(); system( "Pause" ); return 0; }
《More Exceptional C++》------ ( 2 )
2010年9月02日 23:12 | Comments(1) | Category:Reading Notes | Tags:
《More Exceptional C++》------ ( 1 )
2010年7月24日 06:15 | Comments(1) | Category:Reading Notes | Tags:
《Exceptional C++》------ ( 2 )
2010年7月09日 01:08 | Comments(1) | Category:Reading Notes | Tags:
条款24:使用/滥用继承
(1):non-public inheritance表示根据某物实作出, Containment标识has-a关系,再隐含表达了根据某物具现出的意思。
inheritance是single containment的超集,无法表达出 N containment的意义。
提倡在能使用aggregation( 聚合 )时,不要使用继承的方法.
(2):什么情况下会使用non-public inheritance:
a),需要改写虚函数,和利用基类的protected成员.
b),有生命期的问题,符合基类和子类的构造和析构的生命期特征.
c),有虚继承关系时,需要使用不同的虚基类初始方法.
d),基类具有 EBCO 性质时. Empty Base Class Optimazation:当空类作为基类时,只要不会与同一类型的另一个对象或子对象分配在同一地址,就不需为其分配任何空间.
e),在子类的member function和 friend中, 可以使用多态.
(3):public inheritance表示Is-a的关系和Work-Like-a的关系,符合LSP法则.所有改写的Member function,都必须不要求更多,也不要求更少(语义,功能代码的一致).
public inheritance并不是为了代码重用这一个简单的目的,它的主要目的为了被既有码以多态形式重复使用base object.
条款25:面向对象程序设计
(1):一些关于C++语言的特色的讨论.
条款26~28:编译期的依赖性
(1):前置声明的使用
(2):采用struct XImpl的方法,来封装类的私有成员.可避免编译依赖性.
条款29:编译级防火墙
Impl方法:把类的私有数据封装成另外一个结构体,而在类中,以一个结构体的指针指向.(隐藏实现细节,提高封装性)
(1):Impl方法的代价: a),给指针的分配内存操作; b),每一个隐藏成员需要一个间接层才可以访问到.
(2):比较好的做法,把所有的私有成员数据和函数都放入Impl中, 并在Impl中存储一个反向找到类的指针.
条款30:"Fast Pimpl"技术
(1)一些关于如何隐藏实现细节类,而使用operator new类似方法的东东.
条款31:名称搜索
(1):Koenig Lookup:如果给函数提供了一个class类型的实参,那么在名称搜索时,编译器会认为包含那个class的命名空间内地同名函数也是候选函数.
条款32~34:接口原则
(1):接口原则: a),对于classX,所有的函数,包括非成员函数,只要满足提到X并且与X同期提供,就是X的接口,是X的逻辑组成部分.
b),成员和非成员函数都是X的接口, 只不过成员具有更强的关联关系而已.
c),同期出现,可解释为出现在同一个头文件或者同一个命名空间中.
条款35~36:内存管理
(1): 常量数据区:存储字符串等在编译期可以确定的值. 在整个程序生命期,区域中的数据都是可见的,并且只是只读的.
(2): 堆,栈,全局/静态区,自由区,略...
(3): 注意new和delete, new[]和delete[], operator new和operator delete的配对使用.
(4): 对于数组,不要使用多态; 对于继承关系, 析构函数要为虚函数.
条款37:auto_ptr
(1): auto_ptr拷贝时,将移交所有权.
(2): 可以声明const auto_ptr<CA> ptr( new CA ), 这样的const auto_ptr在拷贝时会提示错误.
条款38:对象等同问题
(1): 对象指针地址的比较,并不是任何时候都可信度.
条款39:避免自动转换
(1): 隐式转换一般不安全, a) 它会影响重载解析 b) 它会让错误的代码在转换后安静的通过编译.
(2): 应该避免书写转换运算符和单一参数的构造函数.
条款40~41:对象的生命周期
(1): 可以编写一个私有函数来共享拷贝构造和拷贝赋值代码, 不要利用使用显式析构,再placement new的方法来达到目的.
(2): 将赋值函数声明为 T& T::operator=( const T& t ), 返回T& 而不是 const T& 会衍生 ( a = b ) = c 这样的用法问题, 但是带来的好处是
T可以应用到STL的容器中, 因为STL的容器要求 T 的赋值必须返回T&.
条款42:变量的初始化
(1): SomeType t........调用默认构造函数SomeType::SomeType()
SomeType t()......其实是声明了一个函数
SomeType t( u )...拷贝构造
SomeType t = u....也是一个拷贝构造, = 是假象, t在这里不是被赋值, 而是被构造
条款43:正确使用const
(1): 函数返回一个对象时, 尽量使用 const 修饰.
(2): 对于一些改变了内部私有状态的成员函数, 如果对外的逻辑是const的, 依然要声明为const.( 它修改的东西用mutable修饰就可以了 )
条款44:正确使用转型
(1): 所有指针都可隐式转换为 void*。
(2): 向下转型使用dynamic_cast,可以汇报错误( 可以在确定的情况下, 使用static_cast提高效率, 但是如果错误就没办法知道), 向上转型是一种默认的隐式转换.
(3): 对于互不相干的指针转化, 多使用 reinterpret_cast.
(4): dynamic_cast只对public inheritance有效, 可以穿越继承层次交叉转型( 多继承 ).
条款45:bool类型
条款46:转呼叫函数
条款47:控制流
即代码的执行顺序
(1)以下要素都可以影响代码的执行顺序: a),全局变量的初始化顺序 b),函数的参数传参顺序, 执行顺序 c),代码的异常处理
《Exceptional C++》------ ( 1 )
2010年7月08日 01:45 | Comments(3) | Category:Reading Notes | Tags:
条款1:迭代器
(1):无效的迭代器不可提取值, 例如 *end();
(2):对于成对待迭代器注意安全性,是否属于同一容器和是否先后顺序正确( STL的算法不检查这些安全 );
(3):注意迭代器失效的问题;
条款2~3:如何设计不区分大小写的string类
(1):注意string的定义, typedef basic_string<char, char_trait<char>, allocator<char> > string, 只需要继承一个 class my_char_trait : public char_trait<char>
(字面翻译: 改写 字符特性( char trait ) )
条款4~5:具有最大通用性的复用容器
(1):模板构造函数和赋值函数,不会掩盖默认的隐藏的构造函数和赋值函数.
(2):要实现模板类的复制构造和赋值,需要重新实现特殊的成员模板函数,然后调用它( 与默认的形式是不一样的 ).
条款6:临时对象
(1):函数const reference传值, 函数单一入口单一出口,出口处采用显式构造函数返回对象, 使用++i而不是i++.
条款7:尽可能使用STL中的算法.
条款8~17:异常处理的安全性( difficult and fussy ).
条款18~19:代码的复杂性
(1):异常处理,会极大的增加代码的调用路径和复杂性。
条款20: class 设计技术
(1):避免单一参数的构造函数( 包括提供了N-1个默认参数的N参数构造 ),这样会造成隐式转换。
(2):a),函数参数传递采用by const reference not by object; b) 对于a = a + b, 如果支持应写成 a += b, 对于 a,b是非内置类型时,效率提升。
(3):对于运算符重载, 如果编写了 +, 应该同样编写 +=, 并且 + 通过 += 实现, 功能完整,且易于维护.
(4):操作符重载: 一元操作符应该是member function;
形如一元的, =, (), [], -> 应该是member, assignment版操作符( +=, -=, ... )应该是Member function
其他的二元操作符应该是nonmember的.
(5):对于操作符重载,要注意函数的返回类型. 例如 +, 应该返回 const object, 避免 a + b = c出现, 例如 operator <<, 应该返回 stream&, 以便链式等式。
(6):对于类成员名称, 不要使用下划线开头, 例如 _real, 编译器的很多保留字采用这样的命名。
示例类:
//复数类 class CComplex { public: explicit CComplex( double real, double imaginary = 0 ) : m_dbReal( real ), m_dbImaginary( imaginary ) { } CComplex& operator+= ( const CComplex& cref ) { m_dbReal += cref.m_dbReal; m_dbImaginary += cref.m_dbImaginary; return *this; } CComplex& operator++() { ++m_dbReal; return *this; } const CComplex operator++( int ) { CComplex tmp( *this ); ++( *this ); return tmp; } ostream& PrintSelf( ostream& os ) const { return os<<"("<<m_dbReal<<","<<m_dbImaginary<<")"; } private: double m_dbReal; double m_dbImaginary; }; const CComplex operator +( const CComplex& lhs, const CComplex& rhs ) { CComplex tmp( lhs ); return ( tmp += rhs ); } ostream& operator<<( ostream& os, const CComplex& cref ) { return cref.PrintSelf( os ); }
条款21:改写虚函数
(1):在发生了隐藏时, 可以采用 using CBase::Func, 显式的声明父类函数。
(2):虚函数的调用时动态绑定,但是函数的默认参数值,是静态调用, 所以改写虚函数时,不要修改父类的默认参数值.
条款22:类之间的关系-1
(1):public inheritance 代表的是 Is-a 关系, 遵循Liskov替换法则。
LSP--Liskov Substitution Principle
定义:如果对于类型S的每一个对象o1,都有一个类型T的对象o2,使对于任意用类型T定义的程序P,将o2替换为o1,P的行为保持不变,则称S为T的一个子类型。
子类型必须能够替换它的基类型。LSP又称里氏替换原则。
(2):对于要塑造一个 A 是由 B 的实现而产生作用, 应该使用 成员/聚合, 或者私有继承实现。
私有继承出现的场合:是需要Protected成员或者需要修改虚函数的实现。
条款23:类之间的关系-2
(1):关于一些设计模式思想的。
《More Effective C++》------ ( 4 )
2010年6月10日 20:27 | Comments(2) | Category:Reading Notes | Tags:
条款21:通过重载避免隐式转换
(1):C++规定,重载operator时,必须有一个用户自定义类型,即内建类型不允许修改默认的操作符意义。
(2):通过多种形似的操作符重载函数,明确声明需要的类型,可以避免隐式类型转换。
条款22:考虑运算符的赋值形式( op= ),代替它的单独形式( op )
(1):把 +, - 通过+=, -=来实现, a), 易于维护,只需要维护+= 和 -= 代码 b) +=, -=是效率更高的类型
条款23:考虑变更程序库
条款24:理解virtual function, multi derived, virtual base 和 RTTI所需要的代价
(1):虚函数的代价: a) 类多出来一个 vurtual table空间消耗, 实现时, vtbl是否在编译的obj中具现,一是在使用类的所有obj中具现 二是在包含该类的第一个非内联非纯虚 虚函数的实现体。 b) 类对象必须携带以个 vurtual table pointer。 c) 虚函数放弃了 inline 功能。 (动态函数和静态替换时冲突的)
(2):多继承和虚基类的代价: 除了携带自己的vtbl外,还得为基类携带一些特殊的vtbl。
(3):RTTI的代价: 在vtbl的 0 位置,存储了类的 type_info的地址。( 一般化实现方法)
----------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------
技巧( Techniques, 又称Idioms or Pattern )
条款25:将构造函数和非成员函数虚拟化
(1):一种虚拟技巧, 用以个非虚拟函数,去调用一个private或者protected的虚拟函数。
条款26:限制类产生的对象的个数
(1):对象0个或者1个.....Singleten模式。 隐藏构造函数,采用局部静态变量出现
(2):带有局部静态变量的函数,不要使用Inline, 内部链接的函数,可能引起引起复制(包括了局部静态变量)。
(3):对象所处的三个环境: 普通类对象,作为其他类的基类,被其他类包容。
(4):引用计数思想
条款27:要求或者禁止在堆中产生对象
(1):禁止在栈中创建对象: 把析构函数设为 private。但是必须提供成员函数delete。
class COnlyHeap
{ public: COnlyHeap() {} void Destroy() { delete this; } private: ~COnlyHeap() {} }
(2):禁止在堆中创建对象:隐藏掉 operator new() && operator delete()
《More Effective C++》------ ( 3 )
2010年6月08日 05:03 | Comments(4) | Category:Reading Notes | Tags:
《More Effective C++》------ ( 2 )
2010年6月08日 01:10 | Comments(71) | Category:Reading Notes | Tags:
条款6:区别increment / decrement操作符的前置和后置形式
(1):prefix和postfix语法上有区别,因为他们的函数形式表面上一样的,于是实际上 postfix 是多了一个参数的,值始终为0而已。
并且,prefix返回引用,postfix返回const对象(代表旧值,const是避免连加)。
int& operator++() ............. ++i;
const int operator++( int )............. i++(0);
(2):从效率上说,prefix比postfix高;从维护上说,一般的实现版本中,postfix代码调用prefix函数来完成累加动作,这样维护 加动作 只需要维护prefix的函数。
条款7:千万不要重载&&, ||和,操作符
(1): &&, || 有一定的提前判断, ,操作符表示顺序执行,返回最后一个操作的值
(2):该条款告诉你,某些operator不要重载,会破坏C++的语言特性。
不可重载的有:
. .* :: ?:
new delete sizeof typeid
4个cast
条款8:了解不同意义的new 和 delete
(1):new operator, 即new expression, CA *p = new CA; 展开后为: void *buffer = operator new ( size ), 在buffer上构建CA, 调用CA的构造函数
这里的operator new 确实存在, #include <new> ( stl ), void* operator new( size_t t );
(2):new 和 delete 对应, 分配内存并调用构造函数, 析构再删除内存。
如果采用operator new 取得内存,在此内存上产生对象,则不要调用delete, 只需要调用析构,再free内存。 称为 placement new。
void *pBuffer = operator new( sizeof( CA ) ); if ( pBuffer != NULL ) { CA *pA = new( pBuffer ) CA(); //pA正常 //释放方式 //delete pA; //错误 pA->~CA(); free( pBuffer ); } ...
(3):数组, new[ ] 和 delete[ ] 要对应, 调用的constructor 和 destructor数量跟数组的元素个数相关。
(4):new operator和delete operator属于内建操作符,不可修改其行为;但是 operator new 和 operator delete 则可修改其行为。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
异常(Exception)
Exception的好处和理由:
(1):返回错误码的方式,需要函数调用者检查错误,可以被跳过或者忽略掉。而抛出exception,若未被catch,则程序会马上停止;
(2):exception发生时,栈上对象可以析构掉,其他方法无法保证。
条款9:利用Destructor避免资源泄露
(1):本条款的思想就是: 利用exception发生时,析构可以自动调用,避免内存泄露。
容易出错的代码:
... CA *pA = new pA; pA->dosomething(); delete pA; ...
若dosomething()发生异常,则delete无法执行到;
正确的代码:
... CAObject obj( new CA ); obj.GetPtr()->dosomething(); ...
用一种类似于智能指针的思想,以局部变量出现,在析构中清理资源。