最后一章~~拖了几天,得赶紧记下了~~
名字: On the cusp of the object model
7.1 Template
Template用的很少,这节中的有些东西比较晦涩,挑一些能理解的记下吧。剩下的等用的多了再回头来看。
Template的具现行为 template instantiation
Template <calss T>class Point{
public: enum Status{unallocated,normalized};Point(T x=0.0,T y=0.0);
~Point() ; void * operator new(size_t);private:static Point<T> *freelist
static int chunksize; T _x,_y;
};
编译器看到这样的声明,会有什么动作??
没有。static data 不可用 enum 不可用 ,enum虽然类型固定,但他只能和template point class 的某个实体来存取或操作。我们可以 point<float>::Status s;
但是不能 point::status s; 因此我们可能想把enum抽离到一个非模板类以避免多重拷贝。同样道理,freelist 和chunksize对于程序而言也不可用。必须明确指定point<float>::freelist.也就是说静态数据成员是和特定类型的类绑定的而不是泛型的模板类。但是如果定义一个指针不一定会具现出相应的类,因为一个指针,并不一定指向一个class object。编译器不需要知道该class 相应的任何member 或者数据或者
内存布局,所以没有必要具现。但是如果是定义并初始化一个引用就真的会具现出一个实体,因为不存在空引用。
成员函数并不应该被实体化,只有当他被调用的时候才需要被具现出来。
template<class T>
class mumble{
public: Muble(T t=1024):tt(t){ if(tt!=t)throw ex; }
private: T tt;
};
上面的模板中出现错误有,t=1024 不一定成功,!= 不一定定义
这两个错误只有到具现操作结合具体类型才能确定出来
编译器面对一个template声明,在他被一组实际参数具现之前,只能实行以有限地错误检查,只有特定实体定义之后,才会发现一些与语法无关的但是十分明显的错误,这是技术上的一大问题。
Template 的名称决议方式
.h文件定义
void out(int x){
cout<<"out_by_class_define_scrope"<<endl;
}
template <class type>
class A{
public:
void test1(){
out(m1);
}
void test2(){
out(m2);
}
private:
int m1;
type m2;
};
.cpp文件定义
void out(double x){
cout<<"out_by_class_instantiation_scrope"<<endl;
}
int main()
{
A<double> a;
a.test1();
a.test2();
}
按照书中的说法,如果一个函数,参数类型和type无关的话,应该取他的scope of template declaration中定义的函数,而反之取在他的instantiation中的那个。事实上在测试中发现
MSVC 中 ,函数定义的决议是依照类型的,如果有合适的函数比如type是double,此时如果定义处或者具现处有double型的函数定义,那么函数就会决议为那一个定义的~~~
MEMber function的具现行为:
编译器如何确保只有一个vtable产生? 一种方法是 每一个virtual func地址都放在vtable中,如果取得函数地址,则表示virtual func 的定义必然出现在程序的某个地点,否则程序无法连接成功。此外该函数只能有一个实体,否则也是连接不成功。那么,就把vtable放在定义了该class的第一个non-inline,nonpure virtual function的文件中吧。。(not so clear)
在实现层面上,template 似乎拒绝全面自动化,以手动方式在个别的object module中完成预先的具现工作是一种有效率的方法。
7.2异常处理
为了维持执行速度,编译器可以在编译时期建立起用于支持的数据结构
为了维持程序大小,编译器可以在执行期建立数据结构
c++ eH 主要由三个部分构成:
throw语句
catch语句
try语句,这些语句可能触发catch子句起作用
一个exception 被抛出时候,控制权会从函数调用释放出来,并寻找一个吻合的catch,如果没有那么默认的处理例程terminate()会被调用,控制权放弃后,堆栈中的每一个函数调用也被推离。每一个函数被推离堆栈的时候,函数的local class object 的dtor也会被调用。
在程序不同段里,由于声明不同的变量,一个区域可能因为在区域内发生exception的处理方式不同分成多个区段。
在程序员层面,eh也改变了函数在资源上的管理。例如下面函数中更含有对一块共享内存的locking和unlocking操作 :
void mumble(void * arena){
Point *p= new point ;
smlock(arena) ;
//…..如果此时一个exception发生,问题就产生了
sumunlock(arena);
delete p;
}
从语义上讲,我们在函数退出堆栈之前,需要unlock共享内存并delete p,我们需要这样做:
try{smlock(arena)} catch(…){
smunlock(arena); delete p; throw;
}
new不需要放在try段里,因为,如果new发生了exception,heap中的内存并没有分配,point的ctor没有调用,如果在ctor中exception,那么piont的任何构造好的合成物也会自动解构掉,heap也会自动释放掉。
处理这种资源管理问题,建议: 把资源需求封装在一个class object 体内,并由dtor释放资源.
auto_ptr<point> ph (new point); smlock sm(arena);//如果此时exception 没有问题
// 不需要明确的unlock 和delete
// local dtor 会在这里被调用 sm.SMlock::~smlock(); ph.auto_ptr<point>::~auto_ptr<point>
Exception handling 的支持:
1.检验发生throw操作的函数
2.决定throw是否发生在try区段里
3.如果是编译器必须把exception type 拿来和catch比较
4.吻合的话控制权交给catch
5.如果throw不发生在try段或者没有吻合的,系统会摧毁所有active local object b从堆栈把当前函数unwind掉 ,跳出到程序堆栈的下一个函数去,然后重复上述步骤
当一个实际对象在程序执行时被抛出,exception object会被产生出来并通常放在相同形式的exception 数据堆栈中。
catch(expoint p){
//do sth
throw;
}
以及一个exception object 类型为 exVertex 派生自 expoint ,这两种类型都吻合,catch会怎么做
以exception object作为初值,像函数参数一样会有一个local copy,如果p是一个object而不是一个reference ,内容被拷贝的时候,这个object的非expoint部分会被切掉,如果有vptr 会被设定为expoint的vptr,如果被再次丢出呢?丢出p需要再产生另外一个exception 临时对象,丢出原来的异常 ,之前的修改统统作废。但是如果 catch(expoint& p);怎么样呢。 任何对这个object的改变都会繁殖到之后的catch语句总。
c++ 编译器为了支持EH付出的代价最大,某种程度是因为执行期的天性以及对底层硬件的依赖。
7.3 RTTI
RTTI是Except handling的一个附属产品,因为我们需要提供某种查询exception objects的方法,用来得到exception的实际类型。
在向下转型问题上,如果要保证其安全性,那么必须在执行期对指针有所查询,看看它到底指向什么类型的对象。那么我们需要额外的空间存储类型信息,通常是一个指针,指某个类型信息节点。
需要额外的时间以决定执行期的类型。
冲突发生在:
1.程序员大量的使用了多台,并需要大量的downcast操作
2.程序员使用内建的数据类型和非多态,他不需要额外负担带来的便利
那么如何了解程序员的需要呢?? 策略是一个具有多态性性质的class,也就是具有内涵继承或者声明 virtual func的类需要rtti支持。
这样所有多态类维护一个vptr。额外负担降低到:每一个class object多花费一个指针,指针只被设定一次,而且是编译器静态设定。
down_cast
if(pfct pf = dynamic_cast< pfct >(pt))….
((type_info*)(pt->vptr[0]))->type_descripter;
Reference --------Pointer
dynamic_cast执行在ptr上 失败返回0,如果实行在ref上。由于ref不能被赋予null,也就是没有空引用。如果我们把一个ref设为0会引发临时对象产生,然后用0初始化它,使ref成为这个临时对象的别名。因此此时失败了会产生一个bad_cast exception。
typeid的参数可以使引用,对象或者是类型
事实上,type_info 也适用内建类型,这对于eh机制有必要
例如 int ex_errno; throw ex_errno;
其中int类型 int *ptr; if(typeid(ptr) == typeid(int*));
----------------------全书笔记到此结束 --------------------------------s