一、new和delete
new和delete 是C++ 动态分配和释放内存的重要方式。当new一个对象时
string *sp=new string();
先分配足够大的内存空间,在调用相应的构造函数来构造对象,最后返回指向对象的指针。
当delete 一个对象时,先调用析构函数,再释放所占的内存空间
delete sp;
若程序想要自行控制内存分配可以定义自己的operator new和operator delete 函数。
//自定义operator new和operator delete
void *operator new(size_t size)
{
if(void* mem=malloc(size))
return mem;
else
throw bad_alloc();
}
void operator delete(void* mem)
{
free(mem);
}
重新定义operator new和operator delete 函数,下次使用new和delete ,编译器会首先寻找类内是否有重新定义的operator new和operator delete 函数,之后再查找全局作用域是否有重新定义的operator new和operator delete 函数,接着再调用标准库的operator new和operator delete 函数。
当类内自定义operator new和operator delete 函数时,默认为static函数,因为operator new发生在对象构造前,而operator delete发生在对象销毁后,不属于对象的生命周期内。
类型size_t 是标准库定义的一种类型,不必传入实参,编译器会为我们计算需要的内存大小,并自行传入size_t形参。
而在实现上C++在底层用的是C语言的malloc和free函数.
二、对象模型的this指针
class Fruit
{
int no;
double weight;
char key;
public:
void print() { }
virtual void process(){ }
};
class Apple: public Fruit
{
int size;
char type;
public:
void save() { }
virtual void process(){ }
};
对于Apple类,拥有Fruit类的全部数据,我们可以用Apple对象对Fruit对象赋值,反之则不行。
Fruit f;
Apple a;
f=a;//编译通过
a=f;//不行,发生错误
从数据内存来看,Apple对象赋值给Fruit对象,相当于发生了数据阶段,把完整的Fruit类的数据赋给了f,这是成立的;若反过来,那么Apple类仅有Fruit类部分数据被覆盖,破坏数据的完整性,可能带来隐患,所以这是不行的。
Fruit* pf=new Apple;
用基类指针指向派生类对象,可以直接用指针操作,实际上控制的数据范围只在Apple对象的Fruit部分,当时带虚函数的类继承后,每个对象在起始位置都会存储一个指向虚函数表的指针,这样就实现了虚函数的挑战!也就是实现类的多态,利用的是派生类都含有基类完整的数据,在一定程度上忽略派生类的区别。
那么关键就是虚函数表真正指向的函数了。用来区分就是实际的this指针,也就是实际的内存是由什么类型所构建的。
在这个事例中,用派生类对象调用基类方法,传入的this指针是指向派生类对象的。
CDocument::OnFileOpen(&myDoc);
所以当运行到Serialize()函数是,编译器查看的虚函数表是属于派生类的,跳转到派生类的Serialize函数,最终调用了派生类对象的自定义部分。