Nonstatic data members被配置于每一个class object内
static data members 被存放在所有的class object之外
static 和 nonstatic function members 也被存放在所有的class oject之外
Virtual functions 分为以下两步:
加上继承:
在之前的基础上再加上一个base class table,在slot中多一个指向此表的相关地址。
虚拟继承(virtual)例如:
class istream:virtual public ios{};
class ostream:virtual public ios{};
class iostream:public istream,public ostream {};
不管在继承串链中派生多少次,永远只有一个实例//
C++中,多态只存在于一个个的public class体系中。
C++支持多态的方式:
1、经由一组隐含的转化操作。例如把一个derived class指针转化为一个指向其public base type的指针:
shape * ps=new circle();
//ps保留了基类的部分
2、经由virtual functions机制:
ps->rotate();
3、经由dynamic_cast和typeid运算符:
if(circle * pc=dynamic<circle*>(ps))....
多态的主要用途是经由一个同用的接口来影响类型的封装,这个接口通常定义在base class中。
“指针类型”会教导编译器如何解释某个特定地址中内存大小。这也是为什么一个类型为void*的指针只能够持有一个地址,而不能通过它操作所指向的object的缘故。
当一个base class object被直接初始化为一个derived class object时,derived object就会被切割以塞入较小的base type内存中,derived type没有留下任何痕迹。
C++通过class的指针和引用来支持多态。
assert(****) //判断内部是否有错,(内部为假)终止程序
malloc()//一个分配动态内存大小的函数,返回void*
strlen() //返回长度,不包含最后的NULL
static_cast<type> (变量) //强行转换不安全(子->父安全)
dynamic_cast<type> (变量) //和上面比更加安全
4种有效的默认构造:
1、带有Default Constructor的Member Class Object
2、带有Default Constructor的Base Class
3、带有一个Virtual Function的Class
4、带有一个Virtual Base Class 的Class
三种使用情况:显式等于,函数参数,函数返回
不会有逐位拷贝的情况:
1、当class内含一个member object而后者的class声明一个copy constructor。
2、当class继承一个含有copy constructor的class
3、当class派生自一个继承串连,其中有一个或者多个virtual base class
4、当class声明一个或者多个virtual function。
X foo()
{
X xx;
if(...)
return xx;
else
return xx;
}
形如foo(),所有return 指令传回相同的对象,编译器会自动对其做NRV优化,方法是以一个参数result取代返回对象:
void foo(X &result)
{
result.X::X();
if(...)
{
//直接处理result
return;
}
else
{
//直接处理result
return;
}
}
对于一句类似于X a = foo()这样的代码,NRV优化后的代码相较于原代码节省了一个临时对象的空间(省略了xx),同时减少了两次函数调用(减少xx对象的默认构造函数和析构函数,以及一次拷贝构造函数的调用,增加了一次对a的默认构造函数的调用)。
在构造函数中对于对象成员的初始化发生在初始化队列中——或者我们可以把初始化队列直接看做是对成员的定义,而构造函数体中进行的则是赋值操作。
初始化队列运行在前(初始化顺序按class中成员声明顺序,而不是初始化队列顺序),构造函数体在后。
编译器实际上会一一操作初始化队列,按顺序在构造函数体内安插初始化操作,并在原来的显式代码之前。
一个inline成员函数躯体之内的一个data member 绑定操作,会在整个class声明完成后才发生。
但是,对于成员函数的参数列表并不是,仍然需要采取保护措施(把“nested type声明”放在class的起始处)
static data member的存取地址是独立出来的指针,而不是对应类成员的指针。
nonstatic data member的存取,利用类的指针位置+offset(偏移量)(这里的偏移量会主动加1,为了区分“一个指向data member的指针,用以指出class 的第一个member"和”一个指向data member的指针,没有指出任何member"两种情况)。
易犯错误:
1、会重复相同的操作
2、把一个class分解为两次或者多层,有可能会膨胀需要的空间。(见p105示例)
多态、继承等
通过地址访问类成员是,其实存在一步内部转化:
func(bmp+sizeof(Base1),pd) //bmp为地址
//防止bmp为0
func(bmp?bmp+sizeof(Base1):0,pd)
通过this来调用
this加上vptr[index]调用
没有this指针
nonmember function\nonstatic function\static function的效率相同
继承深度加深,增加的成本来自constructor
virtual Functions的调用:继承深度加深,增加的成本来自vptr的设定
虚拟机制也可以使用“指向 Member Functions”的指针,只是返回的索引值;
而Member Functions返回的是地址。
面对带来“副作用的实际参数”,需要引入临时对象。
其他的常量表达式先求值再替换
其余的直接代换
需要生成临时对象
当一个class中有纯虚函数,则此类无法拥有实例。对于其中的data member必须有显示的构造函数赋值。
class的data member应该被初始化,只在constructor或者其他的member functions中指定初值。
只能被静态调用
不一定都需要把class中的函数声明为虚拟函数,视函数体中的需要而定
const使用看函数的内容是否涉及到需要修改data member
只有一个完整的class object被定义出来,它才能被调用。如果object只是某个完整object的subobject,它就不能被调用。
在一个class的constructor中,经构造出来的对象来调用一个virtual function,其函数实例应该在此class内。
vptr的初始化在base class constructor调用操作后,在程序员提供的代码或者是“member initialization list”所列的members初始化之前。
vptr必须被设定的两种情况:
1、当一个完整的对象被构建出来。例如Point a;其中的constructor必须设定vptr
2、当一个subobject constructor调用一个virtual function。
一个class对于默认的copy assignmemt operator,在下面的情况不会表现bitwise copy语义:
1、当class内含一个member object而后者的class声明一个copy assignment operator。
2、当class继承一个含有copy assignment operator的class
3、当class派生自一个继承串连,其中有一个或者多个virtual base class
4、当class声明一个或者多个virtual function。
总结:尽量不要允许一个virtual base class 的拷贝操作。甚至不要在任何virtual base class中声明数据。
只有在class内含的member object(或者是class自己的base class中)拥有destructor,才会自动合成一个destructor。
destructor的扩展顺序:p235
尽量把object的声明放在使用它的程序段的附近
在程序启动的时候,才会调用所包含的constructor,因此需要静态初始化
p244图
建议:不要使用那些需要静态初始化的global objects。
编译器内部使静态变量只在调用(需要)的时候被构造出来。
Point2w *ptw=new (area) Point2w
area指向内存的一块区域,用来放置新产生的Point2w对象