new 关键字创建出来的对象位于什么地方?
new首先获取足够大的内存空间,默认为堆空间,在获取的空间中调用构造函数创建对象。delete 调用析构函数销毁对象,归还对象所占的空间
在 C++ 中能够重载 new/delete 操作符,推荐在类中进行局部重载,默认为静态成员函数,不写 static 也是静态成员函数,不推荐全局重载,重载的意义在于改变对象创建时的内存分配方式
有了 new/delete 重载,就可以改变对象创建时的内存分配方式,不一定在堆中分配内存,可以在静态存储区,栈中创建对象。
1 静态存储区中创建对象
在类中使用 static 成员变量定义静态存储区的空间,并定义标志,用于表示每块内存是否被占用,空闲为 0,使用中为 1。new 申请空间时,在静态存储空间中寻找一个空闲空间,返回地址。delete 时,检查内存合法后将标志置为 0,表示内存不使用了。
// 52-1.cpp
#include <iostream>
#include <string>
using namespace std;
class Test
{
static const unsigned int COUNT = 4; // 最多存放的对象数量
static char c_buffer[]; // 对象的存储空间
static char c_map[]; // 每个存储对象位置是否空间,0表示空间可用
int m_value;
public:
void* operator new (unsigned int 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; // 标志置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;
}
主函数中 Test* pa[5] 申请 5 个对象的空间,实际最多只能申请 4 个,所以最后一个申请不成功。
编译运行:
==== Test Single Object ====
succeed to allocate memory: 00CBC5AC
succeed to free memory: 00CBC5AC
==== Test Object Array ====
succeed to allocate memory: 00CBC5AC
pa[0]00CBC5AC
succeed to allocate memory: 00CBC5B0
pa[1]00CBC5B0
succeed to allocate memory: 00CBC5B4
pa[2]00CBC5B4
succeed to allocate memory: 00CBC5B8
pa[3]00CBC5B8
pa[4]00000000
delete 00CBC5AC
succeed to free memory: 00CBC5AC
delete 00CBC5B0
succeed to free memory: 00CBC5B0
delete 00CBC5B4
succeed to free memory: 00CBC5B4
delete 00CBC5B8
succeed to free memory: 00CBC5B8
delete 00000000
2 在指定地址上创建对象
如何在指定地址上创建 C++ 对象?
// 52-2.cpp
#include <iostream>
#include <string>
using namespace std;
class Test
{
static unsigned int c_count;
static char* c_buffer;
static char* c_map;
int m_value;
public:
static bool SetMemorySource(char* memory, unsigned int 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 (unsigned int 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);
}
}
}
};
unsigned int 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;
}
main 函数中的 buffer[12] 在栈中分配空间,将这个地址指定为 new 申请对象的地址,则 new 在栈中分配空间。
C++ 类中成员函数,static 变量不计算入 sizeof,buffer[12] 大小为 12 个字节,Test 大小为 4 字节,所以最多可以放置三个对象,我们申请了 5 个对象,所以只有前三个对象对申请成功。
编译运行
==== Test Single Object ====
succeed to allocate memory: 00B5FE20
succeed to free memory: 00B5FE20
==== Test Object Array ====
succeed to allocate memory: 00B5FE20
pa[0]00B5FE20
succeed to allocate memory: 00B5FE24
pa[1]00B5FE24
succeed to allocate memory: 00B5FE28
pa[2]00B5FE28
pa[3]00000000
pa[4]00000000
delete 00B5FE20
succeed to free memory: 00B5FE20
delete 00B5FE24
succeed to free memory: 00B5FE24
delete 00B5FE28
succeed to free memory: 00B5FE28
delete 00000000
delete 00000000
3 小结
1、new/delete 操作符可以重载,推荐针对具体类重载
2、new 默认在堆上分配空间,重载可以在静态存储区、栈上分配空间