【C++ Primer | 19】控制内存分配

本文深入探讨了C++中自定义new和delete运算符的方法,以及定位new表达式的使用技巧。通过实例代码,详细解析了全局与类级别的new和delete重载函数的工作原理,展示了如何在预分配的内存上构造对象,以提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 测试代码: 

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;

void* operator new(size_t size)
{
    cout << "global Override operator new" << endl;
    if (void* ptr = malloc(size))  
        return ptr;
    else
        throw bad_alloc();
}

void* operator new(size_t size, int flag)
{
    cout << "global Override operator new: " << flag << endl;
    return (::operator new(size));
}


void operator delete (void* ptr)
{
    cout << "global Override operator delete" << endl;
    free(ptr);
    ptr = nullptr;
}

void operator delete (void* ptr, int flag)
{
    cout << "Override operator delete: " << flag << endl;
    ::operator delete(ptr);
    ptr = nullptr;
}

int main()
{
    int* ptr = new int(10);
    delete ptr;
    cout << endl << "------------" << endl << endl;
    ptr = new(20) int(10);
    delete ptr;
    return 0;
}

输出结果:

分析:

从上面的结果可以看出,new int(10);直接先调用 operator new(size_t size); 由于int没有构造函数,在那块内存上调用int的构造函数; 在delete ptr; 的时间直接调用 operator delete(void * ptr);这个函数

new(20) int(10);的时候,则调用重载版本的 operator new(size_t size, int flag); 而该函数有调用了 operator new(size_t size); 函数,释放的时候delete ptr;还是直接只调用operator delete(void * ptr);(注:这里初步提出为啥不调用operator delete(void * ptr, int flag); 这个函数来释放ptr ???因为它的用途不在这,而在于下面将要讲的。

 

2. 测试代码:

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;

void* operator new(size_t size)
{
    cout << "global Override operator new" << endl;
    void* ptr = malloc(size);
    return ptr;
}

void* operator new(size_t size, int flag)
{
    cout << "global Override operator new: " << flag << endl;
    return (::operator new(size));
}

void operator delete (void* ptr)
{
    cout << "global Override operator delete" << endl;
    free(ptr);
    ptr = nullptr;
}

void operator delete (void* ptr, int flag)
{
    cout << "Override operator delete: " << flag << endl;
    ::operator delete(ptr);
    ptr = nullptr;
}

class Base
{
public:

    Base()
    {
        cout << "Base construct" << endl;
        throw 2;
    }
    ~Base()
    {
        cout << "Base destructor" << endl;
    }

    //类中定制的operator new会覆盖全局的函数,但可以通过简单的调用全局的函数来实现调用
    static void* operator new(size_t size)
    {
        cout << "operator new of Base" << endl;
        return ::operator new(size); //调用全局的operator new
    }

    static void* operator new(size_t size, int flag)
    {
        cout << "Override operator new of Base: " << flag << endl;
        return operator new(size);
    }

    static void operator delete(void* ptr)
    {
        cout << "Operator delete of Base" << endl;
        ::operator delete(ptr);
    }

    static void operator delete(void* ptr, int flag)
    {
        cout << "Override operator delete of Base: " << flag << endl;
        operator delete(ptr);
    }

    int x;
    int y;
};
int main()
{
    try
    {
        Base* bptr = new(20) Base;
    }
    catch (...)
    {
        cout << "catch a exception" << endl;
    }
    return 0;
}

输出结果:

 

2. 若是不给Base类重载 static void operator delete(void * ptr, int flag);这个函数,结果则如下图:

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;

void* operator new(size_t size)
{
    cout << "global Override operator new" << endl;
    void* ptr = malloc(size);
    return ptr;
}

void* operator new(size_t size, int flag)
{
    cout << "global Override operator new: " << flag << endl;
    return (::operator new(size));
}

void operator delete (void* ptr)
{
    cout << "global Override operator delete" << endl;
    free(ptr);
    ptr = nullptr;
}

void operator delete (void* ptr, int flag)
{
    cout << "Override operator delete: " << flag << endl;
    ::operator delete(ptr);
    ptr = nullptr;
}

class Base
{
public:

    Base()
    {
        cout << "Base construct" << endl;
        throw 2;
    }
    ~Base()
    {
        cout << "Base destructor" << endl;
    }

    //类中定制的operator new会覆盖全局的函数,但可以通过简单的调用全局的函数来实现调用
    static void* operator new(size_t size)
    {
        cout << "operator new of Base" << endl;
        return ::operator new(size); //调用全局的operator new
    }

    static void* operator new(size_t size, int flag)
    {
        cout << "Override operator new of Base: " << flag << endl;
        return operator new(size);
    }

    static void operator delete(void* ptr)
    {
        cout << "Operator delete of Base" << endl;
        ::operator delete(ptr);
    }

    int x;
    int y;
};
int main()
{
    try
    {
        Base* bptr = new(20) Base;
    }
    catch (...)
    {
        cout << "catch a exception" << endl;
    }
    return 0;
}

输出结果:

 

 

二、定位new表达式

operator new和operator delete和alloctor类的allocate和deallocate很像,都是负责分配和释放内存的函数,但是对于operator new分配的内存空间我们无法使用construct函数构造对象,我们应该使用new的定位new形式构造对象。

1. 测试代码:

#include <iostream>
#include <new>
using namespace std;

const int chunk = 16;
class Foo
{
public:
    int val() { return _val; }
    Foo() { _val = 0; }
private:
    int _val;
};

//预分配内存,但没有Foo对象
char* buf = new char[sizeof(Foo) * chunk];
int main(void)
{
    //在buf中创建一个Foo对象
    Foo* pb = new (buf) Foo;
    //检查一个对象是否被放在buf中
    if (pb->val() == 0)
    {
        cout << "new expressio worked!" << endl;
    }
    //到这里不能再使用pb
    delete[] buf;
    return 0;
}

placement new的作用就是:创建对象但是不分配内存,而是在已有的内存块上面创建对象。用于需要反复 创建并删除的对象上,可以降低分配释放内存的性能消耗。定位new表达式(placement new expression),允许程序员将对象创建在已经被分配好的内存中,new表的式的形式如下:

new (place_address) type
new (palce_address) type (initializer-list)

【Note】: 

  • 当只传入一个指针类型的实参时,定位new表达式构造对象但是不分配内存,这个指针没有要求,甚至可能是一个不是一个指向动态内存的指针
  • 调用析构函数会销毁对象,但是不会释放内存。

 

2. 测试代码:

#include <iostream>
using namespace std;
char addr1[100];
int main()
{
    cout << "******定位new表达式演示***by David***" << endl;
    char addr2[100];
    char *addr3 = new char[100];
    cout << "addr1 = " << (void*)addr1 << endl;
    cout << "addr2 = " << (void*)addr2 << endl;
    cout << "addr3 = " << (void*)addr3 << endl;
    int *p = nullptr;
    
    p = new(addr1)int;  ////把内存分配到静态区
    *p = 1;
    cout << (void*)p << "  " << *p << endl;
    
    p = new(addr2)int; ////把内存分配到栈区
    *p = 2;
    cout << (void*)p << "  " << *p << endl;
    
    p = new(addr3)int;  //把内存分配到堆区
    *p = 3;
    cout << (void*)p << "  " << *p << endl;
    return 0;
}

输出结果:

 

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值