|
|
@@ -411,3 +411,206 @@ int main() {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
+### 分配内存
|
|
|
+
|
|
|
+```cpp
|
|
|
+Foo* p = new Foo();
|
|
|
+
|
|
|
+delete p;
|
|
|
+```
|
|
|
+
|
|
|
+上述代码等价于下面的代码,原理不再解释
|
|
|
+
|
|
|
+```cpp
|
|
|
+Foo* p = (Foo*)operator new(sizeof(Foo));
|
|
|
+new(p) Foo(x);
|
|
|
+
|
|
|
+p->~Foo();
|
|
|
+operator delete (p);
|
|
|
+```
|
|
|
+
|
|
|
+`operator new` 本质上还是调用 `malloc` 来分配内存,虽然这个过程一般来说很快,但是如果可以减少 `malloc` 调用的次数,一定程度上可以加快程序运行效率
|
|
|
+
|
|
|
+那么我们可以一开始就分配一个很大的内存,然后切成一个个小块分配给具体的对象,这样就只用 `malloc` 一次,然后我们手动对内存分块
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+还有一点就是这样子可以减少 `cookie` 的量,每一次 `malloc` 除了分配指定大小的内存之外,还在内存块前后分别额外申请了内存用于存放 `cookie`,`cookie`中的信息用于表示内存**是否被分配**以及**分配的大小**等信息
|
|
|
+
|
|
|
+> `cookie` 并不是C++语言标准中的术语
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+如果是一开始自己申请一大块内存,手动切割内存可以减少 `cookie` 的量,从而提升内存的使用率
|
|
|
+
|
|
|
+基于上面的想法,我们得到了如下的设计
|
|
|
+
|
|
|
+```cpp
|
|
|
+#include <cstddef>
|
|
|
+#include <iostream>
|
|
|
+
|
|
|
+class Screen {
|
|
|
+public:
|
|
|
+ Screen(int x): i(x) {};
|
|
|
+ int get() { return i; }
|
|
|
+
|
|
|
+ static void* operator new(size_t);
|
|
|
+ static void operator delete(void*, size_t);
|
|
|
+
|
|
|
+private:
|
|
|
+ Screen* next;
|
|
|
+ static Screen* freeStore;
|
|
|
+ static const int screenChunk;
|
|
|
+
|
|
|
+private:
|
|
|
+ int i;
|
|
|
+};
|
|
|
+
|
|
|
+Screen* Screen::freeStore = nullptr;
|
|
|
+const int Screen::screenChunk = 24;
|
|
|
+
|
|
|
+void* Screen::operator new(size_t size)
|
|
|
+{
|
|
|
+ Screen* p = nullptr;
|
|
|
+ if (!freeStore) {
|
|
|
+ // linked list 为空 需要申请一大块内存
|
|
|
+ size_t chunk = screenChunk * size;
|
|
|
+ freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
|
|
|
+ // &freeStore[screenChunk - 1] 表示 freeStore 数组中最后一个 Screen 的内存地址 p 指向最后一个 Screen 时表示遍历结束
|
|
|
+ for (; p != &freeStore[screenChunk - 1]; ++p) {
|
|
|
+ p->next = p + 1;
|
|
|
+ }
|
|
|
+ p->next = nullptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ p = freeStore;
|
|
|
+ freeStore = freeStore->next;
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+void Screen::operator delete(void* p, size_t)
|
|
|
+{
|
|
|
+ (static_cast<Screen*>(p))->next = freeStore;
|
|
|
+ freeStore = static_cast<Screen*>(p);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```cpp
|
|
|
+int main() {
|
|
|
+ std::cout << sizeof(Screen) << std::endl;
|
|
|
+
|
|
|
+ size_t const N = 100;
|
|
|
+ Screen* p[N];
|
|
|
+
|
|
|
+ for (int i = 0; i < N; ++i) {
|
|
|
+ p[i] = new Screen(i);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < 10; ++i) {
|
|
|
+ std::cout << p[i] << std::endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < N; ++i) {
|
|
|
+ delete p[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+在自定义 `opeator new` 和 `operator delete` 之前,连续申请100个 Screen,每个对象的地址信息,可见虽然是连续申请的对象,但是地址之间插值并不是 16,可见有 cookie 存在
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+在使用自定义 `operator new` 和 `operator delete` 之后,Screen 对象地址连续,并没有被 Cookie 占用
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+这里 Screen 使用了链表来连接整个大内存空间中的 Screen 对象
|
|
|
+
|
|
|
+有一个问题就是 Screen 对象中仅仅存在一个 `int i` 的属性大小是 8 个字节,但是为了自己分配内存空间需要额外定义 `Screen* next` 指针,指针也占用 8 个字节,浪费了 50% 的空间
|
|
|
+
|
|
|
+为了节省指针导致的内存浪费,可以使用 `union` 来处理
|
|
|
+
|
|
|
+```cpp
|
|
|
+class Airplane {
|
|
|
+private:
|
|
|
+ struct AirplaneRep {
|
|
|
+ unsigned long miles;
|
|
|
+ char type;
|
|
|
+ };
|
|
|
+
|
|
|
+private:
|
|
|
+ union {
|
|
|
+ AirplaneRep rep; // 指针对使用中的 objects
|
|
|
+ Airplane* next; // 指针对 fire list 中的object
|
|
|
+ };
|
|
|
+
|
|
|
+public:
|
|
|
+ unsigned long getMiles() { return rep.miles; }
|
|
|
+ char getType() { return rep.type; }
|
|
|
+
|
|
|
+ void set(unsigned long m, char t) {
|
|
|
+ rep.miles = m; rep.type = t;
|
|
|
+ }
|
|
|
+
|
|
|
+public:
|
|
|
+ static void* operator new (size_t size);
|
|
|
+ static void operator delete (void* deadObject, size_t size);
|
|
|
+
|
|
|
+private:
|
|
|
+ static const int BLOCK_SIZE;
|
|
|
+ static Airplane* headOfFreeList;
|
|
|
+};
|
|
|
+
|
|
|
+Airplane* Airplane::headOfFreeList = nullptr;
|
|
|
+const int Airplane::BLOCK_SIZE = 512;
|
|
|
+
|
|
|
+void* Airplane::operator new(size_t size) {
|
|
|
+ // 如果大小有误 转交 ::operator new
|
|
|
+ if(size!=sizeof(Airplane)) {
|
|
|
+ return ::operator new(size);
|
|
|
+ }
|
|
|
+
|
|
|
+ Airplane* p = headOfFreeList;
|
|
|
+ if (p) {
|
|
|
+ headOfFreeList = p->next;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // free list 为空 需要申请初始内存
|
|
|
+ Airplane* newBlock = static_cast<Airplane*>(::operator new(BLOCK_SIZE * sizeof(Airplane)));
|
|
|
+
|
|
|
+ // 将小块串成 free list
|
|
|
+ // 但跳过 #0 因为它将作为本次返回结果
|
|
|
+ for (int i = 1; i < BLOCK_SIZE - 1; i++)
|
|
|
+ {
|
|
|
+ newBlock[i].next = &newBlock[i + 1];
|
|
|
+ }
|
|
|
+ newBlock[BLOCK_SIZE - 1].next = nullptr;
|
|
|
+ p = newBlock;
|
|
|
+ headOfFreeList = &newBlock[1];
|
|
|
+ }
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+void Airplane::operator delete(void* p, size_t size)
|
|
|
+{
|
|
|
+ if (p == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (size != sizeof(Airplane)) {
|
|
|
+ ::operator delete(p);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Airplane* carcass = static_cast<Airplane*>(p);
|
|
|
+ carcass->next = headOfFreeList;
|
|
|
+ headOfFreeList = carcass;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+`Airplane` 的 `sizeof` 大小为 8
|
|
|
+
|
|
|
+
|
|
|
+
|