一.对象模型(Object Model):关于虚指针和虚表
现给出两个类,我们从他们的内存构成来展开讨论
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(){ }
};
以下是用来测试的代码
#include <iostream>
//#pragma pack(1)
using namespace std;
class Fruit{
int no;
double weight;
char key;
public:
explicit Fruit(int no_ = 0,double weight_ = 0,char key_='a' ):no(no_),weight(weight_),key(key_){}
void print() {
cout<<this<<' '<<&no<<' '<<&weight<<' '<<static_cast<void*>(&key)<<endl;
}
virtual void process(){ cout<<this<<'\n'<<&no<<'\n'<<&weight<<'\n'<<static_cast<void*>(&key)<<'\n'<<endl; }
};
class Apple: public Fruit{
int size;
char type;
public:
Apple(int size_=0, char type = 'a'){}
void save() { }
virtual void process(){ Fruit::process ();cout<<&size<<'\n'<< static_cast<void*>(&type)<<'\n'<<endl; }
};
void getVtrlAddr(const Fruit& fruit);
void getVtrlAddr (const Fruit &fruit) {
printf("虚表地址:%p\n", *(int *)&fruit);
}
int main() {
using namespace std;
Fruit f;
Apple a;
cout<<"Fruit 内存地址排布:"<<endl;
f.process ();
cout<<"Apple 内存地址排布:"<<endl;
a.process ();
cout<<"f的size"<< sizeof (f)<<endl;
cout<<"a的size"<< sizeof (a)<<endl;
getVtrlAddr (f);
getVtrlAddr (a);
return 0;
}
得出结果(64位操作系统下)
以Fruit为例this的地址是0x7fff53ca4b28,第一个成员变量的no的地址0x7fff53ca4b30,差了8,说明0x7fff53ca4b28~0x7fff53ca4b29这块内存储存着一个数据,就是一个虚指针,在64位上的大小是8.因为该class有虚函数,所以有虚指针指向由虚函数构成的一个表.
我使用下列函数获取Fruit的虚指针,这个函数有效的前提存在虚指针.
void getVtrlAddr (const Fruit &fruit) {
printf("虚表地址:%p\n", *(int *)&fruit);
}
至于为什么要对齐
大部分的参考资料都是如是说的:
二.const
关于const考虑下图
需要注意的是当成员函数的const和non-const版本同事存在,const object只会调用const版本,non-const ovject 只会调用non-const版本.
关于const不同位置的意义:
char greeting[] = "Hello";
char *p = greeting;//non-const pointer,non-const data p可以指向别处,*p也可以改变
const char*p = greeting;//non-const pointer ,const data p可以指向别处,*p不可以改变
char *const p = greeting;//const pointer,non-const data p不可以指向别处,*p可以改变
const char *const p = greeting;//const pointer,const data p不可以指向别处,*p也不可以改变
将某些东西声明为const可帮助编译器侦测出错误的用法.const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体等.
编译器强制实施bitwise constness,但我们写程序时应该使用"概念上的常量性"(conceptual constness)
const 和non-const 成员函数中避免重复,如果二者有着实质上等价的实现时,令non-const版本调用const版本可避免代码重复三.new和delete重载
首先,new和delete是运算符,重载new和delete是可能的.这样做的原因是,有时候希望使用某种特殊的动态内存分配方法.例如,可能有些子程序,他们的堆已经耗尽,自动开始把一个磁盘文件当虚存储使用,或者用户希望控制某一片存储空间的分配等.
重载new和delete的一般格式如下
void *operator new (size_t size)
{
......//完成分配工作
return pointer_to_memory;
}
void operator dete(void *p)
{
......//释放由p指向的存储空间
}
重载new[] delete[]同上
注意:
new是先分配memory再调用ctor,delete是先调用dtor再释放memory.array new 一定搭配array delete
还有一种member operator new/delete
形如 void * operator new(size_t); void operator delete (void*,size_t);void * operator new[](size_t); void operator delete[] (void*,size_t);
要想拒绝使用重载的new和delete函数,而使用全局的new和delete 请在new和delete前加"::" 就变成"::new","::delete".
我们可以重载class member operator new(),写出多个版本,前提是每一版本的声明都必须有独特的参数列,其中第一参数必须是size_t,其余参数以new所指定的placement arguments为初值.出现于new(...)小括号内的便是所谓的placement arguments.
只有当new所调用的ctor跑出exception,才会调用这些重载版的operator delete().它只可能这样被调用,主要用来归还未能完全创建成功的object所占用的memory.