C++中的动态内存分配
- C++中通过new关键字进行动态内存申请【C语言malloc是一个库函数,C语言自身不包含动态内存申请,一些编译器可能不支持malloc】
- C++中的动态内存申请是基于类型进行的【malloc基于字节数分配】
- delete关键字用于内存释放
1 2 3 4 5 6 7 8 9
| Type* pointr = new Type;
delete pointer;
Type* pointer = new Type[N];
delete[] pointer;
|
注意:释放数组必须加上[],delete[] pointer;
如果没加上[],释放的仅仅是数组第一个元素,后面的一大块内存都没有释放
new关键字与malloc函数的区别
- new关键字是C++的一部分
malloc是由C库提供的函数一些编译器可能没有malloc函数的库
- new以具体类型为单位进行内存分配
malloc以字节为单位进行内存分配
- new在申请单个类型变量时可进行初始化
malloc不具备内存初始化的特性
delete 和 free 的区别
- delete 在所有 C+ + 编译器中都被支持
free 在某些系统开发中是不能调用
- delete 能够触发析构函数的调用
free 仅归还之前分配的内存空间
- 对象的销毀只能使用 delete
free 不适合面向对象开发
1 2 3 4
| int main(){ Test* pn = new Test; }
|
new关键字的初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <stdio.h>
int main() { int* pi = new int(1); float* pf = new float(2.0f); char* pc = new char('c');
printf("*pi = %d\n", *pi); printf("*pf = %f\n", *pf); printf("*pc = %c\n", *pc); delete pi; delete pf; delete pc; return 0; }
|
1 2 3 4 5
| fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test *pi = 1 *pf = 2.000000 *pc = c
|
C++中的命名空间
- 在C语言中只有一个全局作用域
C语言中所有的全局标识符共享同一个作用域
标识符之间可能发生冲突
- C++中提出了命名空间的概念
命名空间将全局作用域分成不同的部分
不同命名空间中的标识符可以同名而不会发生冲突
命名空间可以相互嵌套
全局作用域也叫默认命名空间
1 2 3 4 5 6 7 8 9
| namespace Name { namespace Internal { } }
|
C++命名空间的使用:
- 使用整个命名空间:
using namespace name;
- 使用命名空间中的变量:
using name::variable;
- 使用默认命名空间中的变量:
::variable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h>
namespace First { int i = 0; }
namespace Second { int i = 1; namespace Internal { struct P { int x; int y; }; } }
int main() { using namespace First; using Second::Internal::P; printf("First::i = %d\n", i); printf("Second::i = %d\n", Second::i); P p = {2, 3}; printf("p.x = %d\n", p.x); printf("p.y = %d\n", p.y); return 0; }
|
1 2 3 4 5 6
| fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test First::i = 0 Second::i = 1 p.x = 2 p.y = 3
|
new和delete分析
面试题:new关键字创建出来的对象位于什么地方?
new/delete 的本质是 C+ + 预定义的操作符
C++对这两个操作符做了严格的行为定义
new :
- 获取足够大的内存空间( 默认为堆空间 )
- 在获取的空间中调用构造函数创建对象
delete:
- 调用析构函数销毀对象
- 归还对象所占用的空间( 默认为堆空间 )
在 C++ 中能够重载 new / delete 操作符
- 全局重载( 不推荐 )
- 局部重载( 针对具体类进行重载 )
重载 new / delete 的意义在于改变动态对象创建时的内存分配方式
1 2 3 4 5 6 7 8 9 10 11 12 13
| void* operator new (unsigned int size) { void* ret = NULL;
return ret; }
void operator delete (void* p) { }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #include <iostream> #include <string>
using namespace std;
class Test { static const size_t COUNT = 4; static char c_buffer[]; static char c_map[]; int m_value; public: void* operator new (size_t size) { void* ret = NULL; for(int i=0; i<COUNT; i++) { if( !c_map[i] ) { c_map[i] = 1; ret = c_buffer + i * sizeof(Test); cout << "succeed to allocate memory: " << ret << endl; break; } } return ret; } void operator delete (void* p) { if( p != NULL ) { char* mem = reinterpret_cast<char*>(p); int index = (mem - c_buffer) / sizeof(Test); int flag = (mem - c_buffer) % sizeof(Test); if( (flag == 0) && (0 <= index) && (index < COUNT) ) { c_map[index] = 0; cout << "succeed to free memory: " << p << endl; } } } };
char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0}; char Test::c_map[Test::COUNT] = {0};
int main(int argc, char *argv[]) { cout << "===== Test Single Object =====" << endl; Test* pt = new Test; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[5] = {0}; for(int i=0; i<5; i++) { pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl; } for(int i=0; i<5; i++) { cout << "delete " << pa[i] << endl; delete pa[i]; } return 0; }
|
这个程序我们new创建的对象位于全局静态存储区
在指定的地址上创建C++对象
- 在类中重载 new / delete 操作符
- 在 new 的操作符重载函数中返回指定的地址
- 在 delete 操作符重载中标记对应的地址可用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| #include <iostream> #include <string> #include <cstdlib>
using namespace std;
class Test { static size_t c_count; static char* c_buffer; static char* c_map; int m_value; public: static bool SetMemorySource(char* memory, size_t size) { bool ret = false; c_count = size / sizeof(Test); ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char))))); if( ret ) { c_buffer = memory; } else { free(c_map); c_map = NULL; c_buffer = NULL; c_count = 0; } return ret; } void* operator new (size_t size) { void* ret = NULL; if( c_count > 0 ) { for(int i=0; i<c_count; i++) { if( !c_map[i] ) { c_map[i] = 1; ret = c_buffer + i * sizeof(Test); cout << "succeed to allocate memory: " << ret << endl; break; } } } else { ret = malloc(size); } return ret; } void operator delete (void* p) { if( p != NULL ) { if( c_count > 0 ) { char* mem = reinterpret_cast<char*>(p); int index = (mem - c_buffer) / sizeof(Test); int flag = (mem - c_buffer) % sizeof(Test); if( (flag == 0) && (0 <= index) && (index < c_count) ) { c_map[index] = 0; cout << "succeed to free memory: " << p << endl; } } else { free(p); } } } };
size_t Test::c_count = 0; char* Test::c_buffer = NULL; char* Test::c_map = NULL;
int main(int argc, char *argv[]) { char buffer[12] = {0}; Test::SetMemorySource(buffer, sizeof(buffer)); cout << "===== Test Single Object =====" << endl; Test* pt = new Test; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[5] = {0}; for(int i=0; i<5; i++) { pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl; } for(int i=0; i<5; i++) { cout << "delete " << pa[i] << endl; delete pa[i]; } return 0; }
|
new[]和delete[]
new[] / delete[] 与 new / delete 完全不同
- - 动态对象数组创建通过 new[] 完成
- - 动态对象数组的销毁通过 delete[] 完成
- - new[] / delete[] 能够被重载 , 进而改变内存管理方式
注意事项
- - new[] 实际需要返回的内存空间可能比期望的要多
- - 对象数组占用的内存中需要保存数组信息
- - 数组信息用于确定构造函数和析构函数的调用次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <iostream> #include <string> #include <cstdlib>
using namespace std;
class Test { int m_value; public: Test() { m_value = 0; } ~Test() { } void* operator new (size_t size) { cout << "operator new: " << size << endl; return malloc(size); } void operator delete (void* p) { cout << "operator delete: " << p << endl; free(p); } void* operator new[] (size_t size) { cout << "operator new[]: " << size << endl; return malloc(size); } void operator delete[] (void* p) { cout << "operator delete[]: " << p << endl; free(p); } };
int main(int argc, char *argv[]) { Test* pt = NULL; pt = new Test; delete pt; pt = new Test[5]; delete[] pt; return 0; }
|
1 2 3 4 5 6
| fengyun@ubuntu:~/share$ g++ test.cpp -o test fengyun@ubuntu:~/share$ ./test operator new: 4 operator delete: 0x55e56e8b32c0 operator new[]: 28 operator delete[]: 0x55e56e8b32e0
|
1个test对象4字节,5个test对象理应是20个字节,但是观察结果28个字节,多出8个字节是用于存储数组的额外信息