Timothy

链接

《More Exceptional C++》------ ( 3 )

2010年9月03日 22:42 | Comments(1) | Category:Reading Notes | Tags:

 

条款29:auto_ptr
(1):auto_ptr不可用于数组情形.因为它删除内部指针时, 调用delete p,不会delete[] p.( 不要使用auto_ptr<T> t[n] )
(2):C++规定, 0长度数组合法, new T[0], 不会返回NULL, 返回数组地址,只是此数组没有任何元素可以被访问. (0长度数组依然不可以搭配auto_ptr)
 
条款30:智能指针的问题之一
(1):当类含有其他类指针时,注意拷贝问题,注意多次删除问题.
(2):用auto_ptr来包装这个指针,可以来内存分配和释放上有些许便利,但是对于拷贝和赋值依然存在问题.
 
条款31:智能指针问题之二
 
 
条款32:函数指针
(1):用代理类(重载向函数指针的类型转换 )实现 返回指向自己的函数指针.
	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;
}

 

(2):typedef不能递归定义.
 
条款33:模拟嵌套函数
(1):嵌套类:定义在一个类内部的类,可以通过public, private等约束实现类的访问权限控制. 局部类:定义在函数内部的类,只对此函数可见,且不可用于模板参数.
(2):
 
条款34:预处理宏
(1):预处理宏的用处: a),预防头文件的多次包含 b),编译器预处理特性,_LINE_, _FILE_
c),条件编译( #ifdef __DEBUG ) d)去掉一些警告
 
条款35:宏定义
(1):宏常见错误: a),不要忘记给参数加上括号 b)不要忘记给展开式加上括号  c)注意多参数运算的问题  d)避免名字冲突,特别是和库中的函数等 
(2):其他宏需要注意到问题: a), 宏不可递归 b)宏没有地址 c)宏有碍调试
 
 
条款36:初始化
(1):变量初始化尽量使用 T t( u ), 而不是 T t = u.
 
条款37:前置声明
 
条款38:typedef
条款39:using和名字空间
条款40:名字空间使用

《More Exceptional C++》------ ( 2 )

2010年9月02日 23:12 | Comments(1) | Category:Reading Notes | Tags:

 

条款17:构造函数失败和异常
(1):对象的生命期, 从构造函数退出 到 进入析构函数.
(2):如果在对象构造函数就失败了,发生异常, 则说明此对象不曾存在过...
 
 
条款18:构造函数失败: 吸收异常?
(1):C++中,在构造或析构函数中使用 try catch是合法的,( 可以try 成员初始化列表), 但是函数也必须以抛出一个结束, 就算在catch中没写,但是在catch结束时,
也会由系统自动抛出默写一句:throw, 抛出异常结束.
(2):C++中, 只要基类或成员子对象构造失败,整个对象构造就会失败.
(3):在构造函数中使用try catch的意义就是转化 基类异常和子成员异常.( 不需要做所谓的析构处理,它们抛出异常就说明未曾产生 )
(4):析构函数不能抛出异常: 1, 析构就是清理工作,发生异常就是给你捕捉做清理工作,两者是矛盾的 2, 栈碾转机制,
    析构的异常,必须在析构的内部catch住.
    (这是C++标准里的一条)
(5):C++标准里的一条:在构造或者析构的try catch块中,引用对象的任何静态成员或者基类都将导致不可预测行为.
 
 
条款19:uncaught_exception()
(1):少用此函数, 而该写自己强壮的异常处理代码.
 
条款20:未管理指针存在的问题之一:参数求值
(1):形如 f( g( exp1 ), h( exp2 ) ), 表达式exp1, 和 exp2必然先于函数调用前求值, 也可能交叉进行,但是函数调用不可以交叉进行. 所以结果: 1就是exp1和exp2前求值,顺序不计;2就是exp1先求,再g(),然后去计算h(exp2),当然h( exp2 )在前也可以.
(2):多表达式参数函数, 求参时异常容易导致内存泄漏。
 
条款21:未管理指针存在的问题之二:auto_ptr
(1):把表达式求值隐藏到函数调用中,可以避免求值顺序的交叉或者异常导致的一些问题.
(2):把这样的表达式函数参数提出来,做成独立的显式资源分配,效果更好。
 
 
条款22:异常安全和类设计:拷贝赋值
(1):pimpl手法
(2):a),指针上的操作不会抛出异常 b)使用指针却带来内存和资源的问题,应该把他们管理起来(auto_ptr)
 
 
条款23:异常安全和类设计:继承
(1):松散的耦合可以提高安全性(包括异常安全),紧密的耦合降低'最大可能安全性'(包括异常安全). --- 包容 好于 私有继承
 
 
 
条款24:为什么使用多继承
(1):避免多继承自多个 非纯虚基类.  ( 多继承一般适用于 多接口继承)
(2):使用场合:a) 继承多个已存在的库 b) 多接口继承(COM) c)易于多态使用, 子类可以方便替代多个使用基类的地方.
 
 
条款25:如何模拟多继承
 
条款26:多继承和连体双婴问题
(1):多继承自CBase_a,CBase_b,若在两个基类中有同名虚函数 virtual ... FuncV( ... ) [ = 0 ], 要实现子类重写而具有两个功能,方法:
分别从CBase_a, CBase_b派生出代理类,用不同函数调用这个FuncV,子类再从此二者多继承,改写这两个不同的函数实现(子类切不可修改FuncV).
 
条款27:纯虚函数
(1): 纯虚函数也可以出现定义函数体: a),纯虚析构函数  b)提供缺省行为,明确使用 c)提供部分行为 d)
(2): 基类的析构函数,要么应该是虚public函数,要么应该是非虚protected函数(确保不通过基类指针删除子类).
 
 
条款28:受控的多态
(1): 如何让Func1在接受CBase&参数时,可以多态的使用CDerived&, 但是禁止Func2这样:
      声明CDerived私有继承自CBase, 然后Func1是它的 友元.
 
 
 

《More Exceptional C++》------ ( 1 )

2010年7月24日 06:15 | Comments(1) | Category:Reading Notes | Tags:

 

条款一:
 
(1):std::cin和std::cout的类型是 std::istream和std::ostream, 分别是std::basic_istream<char, std::char_traits<char> > 和 std::basic_ostream<char, std::char_traits<char>的typedef。
 
(2):代码要注意可阅读性,避免简洁但却不宜读的代码.
 
(3):提高代码的可扩充性. 保证封装性,关系相分离.
 
 
 
条款二:
 
(1):std::remove(), 是将要删除的元素移到容器(或区间)的末尾,其他元素相应前移,容器(区间)的长度不会改变.  移到末尾的元素,可能保持原值,也可能不会.
 
      这样的原因: remove()是作用在迭代器上的, "从一个容器中删除迭代器所指的元素"这样的迭代器操作不存在。
 
      删除容器的元素应该使用容器的erase方法.
 
 
 
条款三:
 
(1):STL的算法使用仿函数或者函数对象时, 是传值使用。
 
 
 
条款四:
 
(1):trait类: 一个封装了一组类型和函数的类,以使模板类和模板函数可以操纵 实例化类型 的对象.( 实例化类型:即模板实例化时使用的类型)
 
      trait的封装,主要是指把这些数据和接口放在一起,不是指把这些隐藏在某些外壳中,所以trait类一般都是public实现或是一个struct.
 
(2):测试一个 模板类型类 中是否有一个 T* Clone() const的函数
 
     好的实现:
 
 
 
template<typename T>
class CA
{
public:
    ...
    ~CA()
    {
         assert( IsTValid() );
    }
private:
    BOOL IsTValid()
    {
        T* (T::*test) () const = &T::Clone();
        test = NULL;   //避免未使用的变量警告
    }
 
}
 
 
条款5:typename的使用
 
条款6:容器
 
(1):关于取得容器类元素的指针(或者迭代器),要注意迭代器失效的问题.
 
(2):std中,所有的迭代器的*运算,都得到一个容器包含对象的引用
 
(3):vector<bool>的特殊性,伪容器, 在内部存储时,bool类型会优化,将会把8个bool合成一个BYTE存储,这样实际上vector中就是8个元素存储在一起的,
这样,要实现它必须写代理类,在返回标准迭代器和元素时.(这样的应用应该使用 bitset )
 
条款7:vector 和 deque
 
(1):deque是分段连续存储的,他总是在插入式与分配以后的内存, 而vector是在内存满时在分配.
而deque在内存清理时,也比vector费劲.
(2):vs vector:deque提供了push_front, 但是减少了 reserve() 和 capacity() 函数.
(3):如何清除多余的vector内存, vector<T>( vec ).swap( vec )....vec多余的分配内存即清空和释放了(vec.capacity() 等于或只略大于 vec.size() )
 同理, vector<T>().swap( vec ),...把vec的内容和内存都清理( vec.capacity() = 0现在 ) ,对deque容器是同样的.
 
条款8:set 和 map
(1):map<key, value>的迭起器返回的是 pair<const key, value>, 若采用const_cast的方法强制修改key,不会引起map数的重新旋转,会让map失去存储意义.
应该采用 先erase, 再insert的方式, set同理.
 
条款9:相同的代码吗?
(1):避免使用宏, 运算符重载要保持运算符的普通语义.
(2):f( a++ ), 执行顺序是: 先a++, 再用a的旧值掉用 f(),和代码:f( a ); a++; 的区别是: 在f()发生异常时, 第一种++已发生, 第二种还没有
在比如 f = list<T>::erase(), a = iterator中, 第一个合法, 第二个却不合法了,因为erase会引起迭代器失效....特别要注意函数的副作用,安排程序语序.
 
条款10:模板特殊化和重载
(1):非模板函数 优先于 模板函数匹配(不发生隐式转换), 除非是显式的调用模板函数.
 
 
条款11:略
 
条款12:内联
 
条款13:缓式优化之一 - copy on write
(1) 如题
(2) 关于容器内存不够时,扩展内存的策略:
    a),精确增长,   内存分配 O(n), 字符copy动作 O(n), 空间浪费 0
    b),固定长度增加,        O(n),              O(n),         O(1)
    c),指数增加(每次增加0.5当前size)    O(logn) O(1)     O(n)
条款14:缓式优化值二 - 实现copy on write
 
条款15:缓式优化值三 - 完善copy on write
条款16: 多线程环境下的copy on write(
( 13 -16 一个string类的优化 )

《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:

条款10:在constructor内组织资源泄漏。
 
(1):在constructor内初始化指针成员发生异常时,无法调用到析构,于是此成员无法会泄漏。
 
(2):避免方法,try catch,或者把这样的初始化分散到各个private成员函数中,分开捕获异常;最好的方法,把这样的成员封装成智能指针,自动析构。
 
 
 
条款11:不要让异常流出destructor。
 
(1):避免发生栈辗转机制。
 
栈辗转机制:在执行一个函数,catch到一个异常,正在处理时,又抛出一个异常,且没有捕获,传到了上层这个函数,则会发生terminate。
 
(2):要保证所有需要析构的工作都正常完成。
 
 
 
条款12:理解抛出异常和传递一个参数或者调用一个虚函数之间的区别
 
(1):调用一个函数,控制权回到调用者这里;但是抛出一个异常后,控制权不会再回到抛出地点。
 
(2):异常对象一定是以复制对象传递。
 
 
 
条款13:以引用方式传递对象
 
(1):接收到异常对象时,以写 throw; 再次抛出,而不是 throw expobj; 这样会触发再次复制。
 
 
 
条款14:小心使用异常规格( Exception specifications )
 
(1):函数在抛出一个不符合异常规格的异常时,会触发 Unexpected函数,它的缺省行为是terminate, terminate的缺省行为就是 abort.
 
(2):Exception Specifications不检查嵌套性。
 
 
 
条款15:了解异常机制带来的开销
 
 
 
--------------------------------------------------------------------------------------------------------------------------
 
-------------------------------------------------------------
 
效率Efficiency
 
 
 
条款16:牢记80-20法则
 
 
 
条款17:考虑使用Lazy evaluation( 缓式评估)
 
(1): 4个表现方面: a),引用计数,避免不需要的对象拷贝;b),对字符串的 [] 操作符,在读写时区别对待 c), 延缓读写数据库 d), 延缓计算复杂度表达式值
 
(2); 核心思想: 最 缓 的执行某些消耗较大却非必要马上执行的计算或操作,前期以各种技巧规避之。
 
 
 
条款18:分期摊还期望的计算( over-eager evaluation, 急迫式评估 )
 
(1):核心思想:  你认为某个运算或动作需要频繁的执行,则可以采用数据结构或策略高效的处理,降低每次计算的开销。
 
(2):两个例子: cache( 缓存 ) 和  prefeching( 提前读取,预先申请空间)
 
 
 
条款19:理解临时对象的来源
(1):一般有两个主要方式, 为了使函数调用成功执行的隐式转换产生对象 和 函数返回一个对象时。
 
条款20:协助完成对函数返回值的优化
(1):返回对象时,切忌不可返回 栈上对象的引用。
(2):返回对象时,先声明对象,再返回,必将触发复制构造和析构,就是临时对象效率问题,可以 constructor argument
    即  return CA( ... );  //返回一个CA对象 
    原理是: C++允许优化不出现的临时对象(此种返回,对象没有具名实际出现过) --- 返回值优化
 

 

 

《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();
...

 

     用一种类似于智能指针的思想,以局部变量出现,在析构中清理资源。

     

《More Effective C++》------ ( 1 )

2010年6月02日 23:15 | Comments(0) | Category:Reading Notes | Tags: