第18章对于没有多少项目经验的我来说有些东西还真的是不知道实际应该如何应用,这些都涉及设计吧我想。看完这一篇,体会比较深的是内存管理和运行时类型识别,其他的包括类成员指针、嵌套类、局部类、联合等,看完只能说,知道了有这么回事,呵呵。
创建对象一般涉及两个过程,一是分配内存,然后构造对象,这两者相互纠缠在一起,就像对象析构和回收内存一样。构造对象指的是运行构造函数,包括对数据成员进行赋值;析构对象指的是运行析构函数,包括释放构造对象时动态请求的资源。C++中提供了两种方法来完成这些事情:
1. 使用allocator类。
allocator类是一个模板,它提供类型化的内存分配以及对象构造与撤销,下面是该类支持的操作:
Vector类的实现把里面的操作基本都用上了:
#include<memory>
template <class T> class Vector{
public:
Vector():elements(0),first_free(0),end(0){};
void push_back( const T& );
private:
static std::allocator<T> alloc;
void reallocate();
T* elements;
T* first_free;
T* end;
};
template<class T> std::allocator<T> Vector<T>::alloc;
template<class T> void Vector<T>::push_back( const T& t ){
if( first_free == end )
reallocate();
alloc.construct(first_free, t );
++first_free;
}
template<class T> void Vector<T>::reallocate(){
std::ptrdiff_t size = first_free - elements;
std::ptrdiff_t newcapacity = size>0? 2*size : 2;
T* newelements = alloc.allocate(newcapacity);
std::uninitialized_copy(elements, first_free, newelements);
for( T* p=first_free; p != elements; )
alloc.destroy( --p );
if( elements )
alloc.deallocate(elements, end-elements);
elements = newelements;
first_free = elements +size;
end = elements+newcapacity;
}
2. new、delete表达式
当使用new表达式 string *sp = new string( "hello world" ); 的时候,实际上发生三个步骤:
首先,该表达式调用名为operator new的标准库函数,分配足够大的原始的未类型化的内存,以保存指定类型的一个对象;
接着,运行该类型的一个构造函数,用指定初始化式构造对象;
最后,返回指向新分配并构造的对象的指针,但指针类型为void*。
标准库中的operator new和operator delete,它们分配和释放需要大小的原始的、未类型化的内存。
同样,当使用delete表达式 delete sp的时候,实际上也发生了两个步骤:
首先,对sp指向的对象运行适当的析构函数
接着,通过调用名为operator delete的标准函数释放该对象所用的内存。
operator new和operator delete函数有两个重载版本,每个版本支持相关的new表达式和delete表达式:
void* operator new(size_t);
void* operator new[](size_t);
void operator delete( void* );
void operator delete[]( void* );
事实上,还有第三种new表达式,称为定位new(placement new),其形式如下:
new (place_address) type
new (place_address) type( initialized_list );
如alloc.construct( first_free, t );可以改写成:
new (first_free) T(t);
定义new表达式比allocator类的construct成员更灵活,它可以使用任何构造函数并直接建立对象,而construct函数总是使用复制构造函数。
可以用下面两种方式之一,从一对迭代器初始化一个已分配但未构造的string对象:
allocator<string>* alloc;
string* sp = alloc.allocate(2);
new (sp) string(b,e); //construct directly in place
alloc.construct( sp+1, string(b,e) ); //build and copy a temporary
通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检查这些指针或引用所指对象的实际派生类型,有两种操作:
1. typeid操作符,返回指针或引用所指对象的实际类型
2. dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用
这些操作符只为带有一个或多个虚函数的类返回动态类型信息,对于其他类型,返回静态类型的信息。
dynamic_cast操作符一次执行两个操作,它首先验证被请求的转换是否有效,只有转换有效,操作符才实际进行转换。
假设Base是至少带有一个虚函数的类,并且Derived类派生于Base类,basePtr为指向Base的指针,就可以像这样在运行时将它强制转换为指向Derived的指针:
if( Derive * derivePtr = dynamic_cast<Derived*>(basePtr) ){ //如果指针转换失败,derivedPtr=0;
//use Derived object to which derivedPtr points
}else{
//use the Base object to which basePtr points
}
如果引用转换失败,dynamic_cast抛出bad_cast异常
void f( const Base& b ){
try{
const Derived& d = dynamic_cast<const Derived&>(b);
//use Derived object to which derivedPtr points
}catch( bad_cast ){
//use the Base object to which basePtr points
}
typeid要注意的是,typeid的操作数是表示对象的表达式,对于指针,返回的类型是是“指向<静态类型>的指针”
后面的内容就不总结了,我觉得没有一定的项目经验,那些东西的好处其实也是无法体会的,呵呵。
http://philoscience.iteye.com/category/176811