C++中的自定义内存管理

1,问题:

1,new 关键字创建出来的对象位于什么地方?

1,位于堆空间;

2,有没有可能位于其它地方?

1,有;

2,通过一些方式可以使动态创建的对象位于静态存储区;

3,这个存储区在程序结束后释放;

2,new/delete 被忽略的事实:

1,new/delete 的本质是 C++ 预定义的操作符;

1,new/delete 是关键字,但本质是预定义的操作符;

2,C++ 中操作符可以重载;

2,C++ 对这两个操作符做了严格的行为定义;

1,new:

1,获取足够大的内存空间(默认为堆空间);

2,在获取的空间中调用构造函数创建对象;

2,delete:

1,调用析构函数销毁对象;

2,归还对象所占用的空间(默认为堆空间);

3,在 C++ 中能够重载 new/delete 操作符:

1,全局重载(不推荐);

1,实际工程开发中不建议这样做

2,局部重载(针对具体类型进行重载);

1,针对具体的类重载;

3,重载 new/delete 的意义在于改变动态对象创建时的内存分配方式;

1,可以将创建的对象放到其它的内存空间里面去;

4,new/delete 的重载方式:

1,代码示例:

 1 // static member function
 2 void* operator new(unsinged int size)  // 第一步获取内存,参数表示需要获取的内存大小;
 3 {
 4      void* ret = NULL;
 5
 6     /* ret point to allocated memory */  // 第二步在内存中调用构造函数创建对象;
 7
 8     return ret;
 9 }
10
11 // static member function
12 void operator delete (void* p)  // p 指针指向对应的对象地址,也就是要释放的地址;
13 {
14     /* free the memory which is pointed by p */
15 }

2,通过函数来对这两个操作符进行重载;

3,一般针对具体类来重载,所以说 new/delete 的重载函数就是类的成员函数,并且这两个重载函数默认为静态成员函数,写不写 static 都是静态成员函数;

3,静态存储区中创建动态对象编程实验:

 1 #include <iostream>
 2 #include <string>
 3
 4 using namespace std;
 5
 6 class Test
 7 {
 8     static const unsigned int COUNT = 4;
 9
10     static char c_buffer[];  // 本质是这里申请空间而下面只是标记使用而已;
11     static char c_map[];
12
13     int m_value;
14 public:
15     void* operator new (unsigned int size)
16     {
17         void* ret = NULL;  // 如果这片内存已经满了,返回空;
18
19         /* 查找在 c_buffer 里面那些位置是空闲的,可以用来创建 Test 对象 */
20         for(int i=0; i<COUNT; i++)
21         {
22             if( !c_map[i] )  // 当前空间不可用了;
23             {
24                 c_map[i] = 1;  // 标记为不可用;
25
26                 ret = c_buffer + i * sizeof(Test);  // 查找 c_buffer 这片可用内存空间的首地址,并返回这片空间;
27
28                 cout << "succeed to allocate memory: " << ret << endl;
29
30                 break;
31             }
32         }
33
34         return ret;
35     }
36
37     void operator delete (void* p)
38     {
39         if( p != NULL )  // 空指针时候什么都不处理;
40         {
41             char* mem = reinterpret_cast<char*>(p);
42             int index = (mem - c_buffer) / sizeof(Test);  // 得到要释放的动态对象在 c_map 中的位置;
43             int flag = (mem - c_buffer) % sizeof(Test);  // 这些位置必须是固定的,如果 flag 不为 0,指针则不合法;
44
45             if( (flag == 0) && (0 <= index) && (index < COUNT) )
46             {
47                 c_map[index] = 0; // 释放这个地址,即标记这个地址可用;
48
49                 cout << "succeed to free memory: " << p << endl;
50             }
51         }
52     }
53 };
54
55 char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0};  // 定义一块静态的内存空间,内存空间想要存储的是 Test 对象,最多存储 4 个 Test 对象;
56 char Test::c_map[Test::COUNT] = {0};  // 标记数组,用于标记在那些位置已经创建了对象,作用是标记;
57
58 int main(int argc, char *argv[])
59 {
60     cout << "===== Test Single Object =====" << endl;
61
62     Test* pt = new Test;  // 这里是在 c_buffer 里面的静态存储区当中的空间生成的;
63
64     delete pt;
65
66     cout << "===== Test Object Array =====" << endl;
67
68     Test* pa[5] = {0};
69
70     for(int i=0; i<5; i++)
71     {
72         pa[i] = new Test;
73
74         cout << "pa[" << i << "] = " << pa[i] << endl;
75     }
76
77     for(int i=0; i<5; i++)
78     {
79         cout << "delete " << pa[i] << endl;
80
81         delete pa[i];
82     }
83
84     return 0;
85 }

1,结论:

1,new/delete 关键字是可以重载的;

2,重载的意义是改变内存的分配方式,使得动态创建的对象不再位于堆空间里面;

3,这个实验位于自定义的静态存储区里面的 c_buffer 数组当中;

2,拓展:

1,工程中可以结合不同方法来应用 new/delete 特性;

2,将本实验的方法和二阶构造法结合在一起,我们就可以创建一个类,并且规定这个类最多产生多少个对象;

3,单例模式仅仅使得一个类只有一个对象存在,而这里的方法加上二阶构造就可以诞生 N 例模式;

4,问题:

1,如何在指定的地址上创建 C++ 对象?

1,我们已经掌握了在静态存储区里面创建对象,是否可以扩展下这个方法,在任意的地址上创建对象呢?

2,通过重载 new/delete 也许就可以在指定的地址上创建对象;

5,设计思路:

1,在类中重载 new/delete 操作符;

2,在 new 的操作符重载函数中返回指定的地址;

3,在 delete 操作符重载中标记对应的地址可用;

6,自定义动态对象的存储空间编程实验:

  1 #include <iostream>
  2 #include <string>
  3 #include <cstdlib>
  4
  5 using namespace std;
  6
  7 class Test
  8 {
  9     static unsigned int c_count;  // 动态实时做决定,所以这个地方就不能有常量;
 10     static char* c_buffer;
 11     static char* c_map;
 12
 13     int m_value;
 14 public:
 15     /* 动态指定想在什么类型上指定申请对象 */
 16     static bool SetMemorySource(char* memory, unsigned int size)
 17     {
 18         bool ret = false;  // 返回值为 bool 类型,告诉函数调用者,当前动态空间设置是否成功;
 19
 20         c_count = size / sizeof(Test);  // 计算传进来的空间可以创建多少对象;
 21
 22         ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char)))));
 23
 24         if( ret )  // 空间至少为 1,且标记指针合法;
 25         {
 26             c_buffer = memory;  // 将指定空间设置到 c_buffer 上;
 27         }
 28         else  // 一切清零;
 29         {
 30             free(c_map);
 31
 32             c_map = NULL;
 33             c_buffer = NULL;
 34             c_count = 0;
 35         }
 36
 37         return ret;
 38     }
 39
 40     void* operator new (unsigned int size)
 41     {
 42         void* ret = NULL;
 43
 44         /* 有指定的一个具体空间,通过各种计算和验证,看下所指定的空间上面是否可以动态创建对象,标准是 c_count 大于 0,此时意味着通过 setMemorySource() 所指定的空间是可以创建 Test 对象的,则走 if 路径,否则的话,走 else 路径,通过 malloc() 函数得到一片空间; */
 45         if( c_count > 0 )  //
 46         {
 47             for(int i=0; i<c_count; i++)
 48             {
 49                 if( !c_map[i] )
 50                 {
 51                     c_map[i] = 1;
 52
 53                     ret = c_buffer + i * sizeof(Test);
 54
 55                     cout << "succeed to allocate memory: " << ret << endl;
 56
 57                     break;
 58                 }
 59             }
 60         }
 61         else
 62         {
 63             ret = malloc(size);  // 没有指定具体的在那个空间上生成对象时,通过 malloc 来申请默认的堆空间;
 64         }
 65
 66         return ret;
 67     }
 68
 69     void operator delete (void* p)
 70     {
 71         if( p != NULL )
 72         {
 73             if( c_count > 0 )
 74             {
 75                 char* mem = reinterpret_cast<char*>(p);
 76                 int index = (mem - c_buffer) / sizeof(Test);
 77                 int flag = (mem - c_buffer) % sizeof(Test);
 78
 79                 if( (flag == 0) && (0 <= index) && (index < c_count) )
 80                 {
 81                     c_map[index] = 0;
 82
 83                     cout << "succeed to free memory: " << p << endl;
 84                 }
 85             }
 86             else
 87             {
 88                 free(p);  // 和上面对应
 89             }
 90         }
 91     }
 92 };
 93
 94 unsigned int Test::c_count = 0;
 95 char* Test::c_buffer = NULL;
 96 char* Test::c_map = NULL;
 97
 98 int main(int argc, char *argv[])
 99 {
100     char buffer[12] = {0};  // 定义一片栈上空间,用于存放对象;
101
102     Test::SetMemorySource(buffer, sizeof(buffer));
103
104     cout << "===== Test Single Object =====" << endl;
105
106     Test* pt = new Test;
107
108     delete pt;
109
110     cout << "===== Test Object Array =====" << endl;
111
112     Test* pa[5] = {0};
113
114     for(int i=0; i<5; i++)  // 只有 3 个对象的空间,则后两个对象指向为 NULL;
115     {
116         pa[i] = new Test;
117
118         cout << "pa[" << i << "] = " << pa[i] << endl;
119     }
120
121     for(int i=0; i<5; i++)
122     {
123         cout << "delete " << pa[i] << endl;
124
125         delete pa[i];
126     }
127
128     return 0;
129 }

1,通过重载 new/delete,我们可以在任意指定的位置动态创建 C++ 对象;

7,new[]/delete[] 与 new/delete 完全不同:

1,动态对象数组创建通过 new[] 完成;

2,动态对象数组的销毁通过 delete[] 完成;

3,new[]/delete[] 能够被重载,进而改变内存管理方式;

1,这是两个新的操作符;

8,new[]/delete[] 的重载方式:

1,代码示例:

 1 // static member function
 2 void* operator new[] (unsigned int size)
 3 {
 4     rerurn malloc(size);
 5 }
 6
 7 // static member function
 8 void operator delete[] (void* p)
 9 {
10      free(p);
11 }

2,通过类的静态成员函数来重载,不写 static,这两个成员函数在类中也是      静态的;

9,注意事项:

1,nwe[] 实际需要返回的内存空间可能比期望的要多;

1,需要额外的空间来保存数组的信息;

2,如数组长度信息,因为编译器要自动的为我们调用构造函数和析构函数,不保存长度信息,编译器不知道要调用多少次构造函数和析构函数;

2,对象数组占用的内存中需要保存数组信息;

3,数组信息用于确定构造函数和析构函数的调用次数;

10,动态数组的内存管理编程实验:

 1 #include <iostream>
 2 #include <string>
 3 #include <cstdlib>
 4
 5 using namespace std;
 6
 7 class Test
 8 {
 9     int m_value;
10 public:
11     Test()
12     {
13         m_value = 0;
14     }
15
16     ~Test()
17     {
18     }
19
20     void* operator new (unsigned int size)
21     {
22         cout << "operator new: " << size << endl;
23
24         return malloc(size);
25     }
26
27     void operator delete (void* p)
28     {
29         cout << "operator delete: " << p << endl;
30
31         free(p);
32     }
33
34     void* operator new[] (unsigned int size)
35     {
36         cout << "operator new[]: " << size << endl;
37
38         return malloc(size);
39     }
40
41     void operator delete[] (void* p)
42     {
43         cout << "operator delete[]: " << p << endl;
44
45         free(p);
46     }
47 };
48
49 int main(int argc, char *argv[])
50 {
51     Test* pt = NULL;
52
53     pt = new Test;  // operator new: 4;
54
55     delete pt;  // operator delete: 0x8e5d008;
56
57     pt = new Test[5];  // operator new[]: 24;这里多了四个字节,用于保存数组的大小信息,因为编译器自动为我们自调用构造函数和析构函数;
58
59     delete[] pt;  // operator delete[]: 0x8e5d018;
60
61     return 0;
62 }

1,new/delete 和 new[]/delete[] 是完全不同的;

2,通过重载的方式说明了它们的不同;

3,意味着在实际的工程里面,有可能在 new 中函数的内存分配方式和 delete[] 中函数内存分配方式是不一样的,因此必须成对使用,必须要匹配;

4,假设 new[] 动态创建数组是从栈上分配的空间,然后 delete 想要将空间归还到堆空间去,如果交叉使用,则意味着有可能把栈上的空间归还到堆空间上,程序会崩溃,所以要成对出现,不要交叉使用,因为它们 new/delete 和 new[]/delete[] 完全不同;

11,小结:

1,new/delete 的本质为操作符;

2,可以通过全局函数重载 new/delete(不推荐);

3,可以针对具体的类重载new/delete;

4,new[]/delete[] 与 new/delete 完全不同;

原文地址:https://www.cnblogs.com/dishengAndziyu/p/10920093.html

时间: 2024-10-13 02:06:16

C++中的自定义内存管理的相关文章

C++语言学习(二十)——自定义内存管理

C++语言学习(二十)--自定义内存管理 一.统计类对象中成员变量的访问次数 mutable是为了突破const函数的限制而设计的,mutable修饰的成员变量将永远处于可改变的状态.mutable成员变量破坏了只读对象的内部状态,而const成员函数保证只读对象的状态不变性,因此mutable成员变量无法保证只读对象状态的不变性. #include <iostream> using namespace std; class Test { public: Test():m_count(0) {

39-oc集合中对象的内存管理

集合中对象的内存管理 集合的内存管理原则是什么 当把一个对象添加到集合中时,这个对象会做了一次retain操作,计数器会+1 当一个集合被销毁时,会对集合里面的所有对象做一次release操作,计数器会-1 当一个对象从集合中移除时,会对这个对象做一次release操作,计数器会-1 集合方法的普遍规律是什么 如果方法名是add\insert开头,那么被添加的对象,计数器会+1 如果方法名是remove\delete开头,那么被移除的对象,计数器-1

c++中的动态内存管理

c++中的动态内存管理问题 c++中使用new和delete实现动态内存管理.new和delete实现动态管理对象,new[]和delete[]实现动态管理对象数组.c++中的new和delete运算符均使用我们c中学过的malloc和delete函数实现动态内存的开辟. 首先,先简单介绍下c中的几个动态内存函数malloc,realloc,calloc,free; void *malloc(size_t size); //动态内存开辟函数 void free(void *pointer);  

自定义内存管理(五十七)

一个笔试题:编写能统计对象中某个成员变量的访问次数的程序.我们在类中定义一个私有成员变量,在构造函数中初始化为 0,在进行读写操作时都 ++,那么就达到我们的目的了,下面我们看看程序是怎样写的 #include <iostream> #include <string> using namespace std; class Test {     int m_Value;     int m_count; public:     Test(int value = 0)     {   

第69课 技巧,自定义内存管理

如何统计对象中某个成员变量的访问次数? 解法: 这里我们只能满足普通对象的访问统计,那么const对象呢? 完善解法,使得能统计只读对象的访问次数: 使用了mutable之后,只读对象名存实亡. 1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 int m_value; 9 int * const m_pCount; 10 /* mutable int m_

第69课.技巧:自定义内存管理

1.统计对象中某个成员变量的访问次数 注意:对象(普通对象,只读对象) eg: #include <iostream> #include <string> using namespace std; class Test { int m_value; int * const m_pCount; public: Test(int value = 0) : m_pCount(new int(0)) { m_value = value; } int getValue() const { *

NETTY4中的BYTEBUF 内存管理

转 http://iteches.com/archives/65193 Netty4带来一个与众不同的特点是其ByteBuf的重现实现,老实说,java.nio.ByteBuf是我用得很不爽的一个API,相比之下,通过维护两个独立的读写指针,io.netty.buffer.ByteBuf要简单不少,也会更高效一些.不过,Netty的ByteBuf带给我们的最大不同,就是他不再基于传统JVM的GC模式,相反,它采用了类似于C++中的malloc/free的机制,需要开发人员来手动管理回收与释放.从

iOS中引用计数内存管理机制分析

在 iOS 中引用计数是内存的管理方式,尽管在 iOS5 版本号中.已经支持了自己主动引用计数管理模式.但理解它的执行方式有助于我们了解程序的执行原理.有助于 debug 程序. 操作系统的内存管理分成堆和栈. 在堆中分配的内存,都试用引用计数模式:在栈中则不是. NSString 定义的对象是保存在栈中,所以它没有引用计算.看一些书上说它的引用计算会是 fffffffff 最大整数.測试的结果显示它是- 1. 对该对象进行 retain 操作.不好改变它的 retainCount 值. Mut

iOS开发中的ARC内存管理de技术要点

本文旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇文章不是一篇标准的ARC使用教程,并假定读者已经对ARC有了一定了解和使用经验.详细的关于ARC的信息请参见苹果的官方文档与网上的其他教程:) 本文的主要内容: ARC的本质 ARC的开启与关闭 ARC的修饰符 ARC与Block ARC与Toll-Free Bridging ARC的本质 ARC是编译器(时)特性,而不是运行时