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);
// int* pa = new int[1];注意这个是申请数组,数组大小为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 :

  1. 获取足够大的内存空间( 默认为堆空间
  2. 在获取的空间中调用构造函数创建对象

delete:

  1. 调用析构函数销毀对象
  2. 归还对象所占用的空间( 默认为堆空间

在 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;

//allocate memory

return ret;
}

void operator delete (void* p)
{
//free
}
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个字节是用于存储数组的额外信息