【C++深度解析】52、重载new/delete,在静态存储区、栈上分配空间

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 默认在堆上分配空间,重载可以在静态存储区、栈上分配空间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值