本周介绍的是C++中末了剩下几个重要概念,分别是
- 对象模型
- const
- new和delete重载
对象模型
对象模型里面包含了虚表和虚指针,这个又回到了以前分析过的对象在C++内存中的分配。如果class里面有非虚函数,那么这个非虚函数就会在编译时作为静态指针放到class的内存中去。class中如果有虚函数,那么这个时候就会产生很多可能了。虚函数编译时的指针不直接指向函数体本身,它指向一个表(虚表),这个虚表存储了class中虚函数的个数,在调用时,会将this指针通过隐藏参数的方式传递进来。每个带有虚函数的class都会包含一根虚指针,这根指针就在这个类内存的头部存放。调用的时候,碰到对虚函数的调用,就会到对象的头部去找到指针所指向的虚表中去,然后按顺序查找到所需要调用的虚函数之后,将this指针传递进去。
const
const关键字从前只是强调,凡是不打算更改成员变量的成员函数,都应该加const。因为,如果使用者声明了一个const对象,只是调用这个对象的某些不更改数据的函数时,编译器就会报错。
但本周介绍的是更好更通用的一个用法,这里面隐藏着一个编译器给定的一个规则。之前自己写的代码好像也是这么写的,但并不知道为什么编译能通过。
我是写过一个叫做vector的class
template <typename T, unsigned int N>
class vector {
public:
T &operator[](unsigned int index) {
if (index >= 0 && index < N) return component[index];
std::cout << "Error, index out of range";
exit(1);
};
T operator[](unsigned int index) const {
if (index >= 0 && index < N) return component[index];
return T(0);
};
private:
T component[N];
unsigned int _dim;
};
当时只是想着用引用的时候会比较高效,但同时也要考虑到const对象的传值,所以加了一个const的成员函数。
本周我们知道了如果一个class在编写的时候,同时提供了const和非const的成员函数,那么编译器会将const成员函数与const对象合用,将非const成员函数与非const对象合用。
那这件事就很好理解了。需要高效的时候传引用来快速访问数据,需要安全的时候传const值来保护数据。嗯,完美。
new和delete重载
原来new和delete也可以重载。打开了新世界的大门。
当然,new操作会被编译器自动拆分为三个动作
- 调用重载的new
- 将对象指针指向new回来的内存地址
- 调用构造函数
delete操作就分为两步了
- 调用析构函数
- 将对象指针传给重载的delete
new操作符的重载必须写成class::operator new(size_t size);
这种形式,而且第一个参数必须是size_t类型。并且可以有多个不同参数列的new操作重载。与此对应的,就可以有多个不同参数列的delete操作重载与new操作重载对应。当指定的new重载操作调用后遇到构造函数执行错误,就会调用与之对应的delete操作将申请回来的内存还回去。
于是,new操作就给了我们一个偷偷更改分配内存空间大小和如何返回内存地址的方法了。用这个方法,可以自己编写内存池,来动态管理内存。
也有一个办法可以绕过class中重载的operator new(),那就是调用全局的::operator new()操作。
比如
Fruit *f = ::operator new Fruit();