C++的placement new

27 阅读3分钟
  • 参考

  • 普通的 new

    • new 是C++里很常用的操作符号,比如说创建一个对象 A* = new A();,这个操作回执行下面3个步骤
      • 调用 operator new 分配内存,operator new(sizeof(A)),需要注意的是 new 是一个操作符号,operator new 是一个函数,前者不能重载,后者能
      • 调用构造函数 A::A();,进行初始化
      • 返回内存首地址
    • 然后上面说到,operator new 是能重载的,如果没有去重载,调用的则是C++提供的默认的 operator new(size_t),如果想下面代码一样重载了
    • 那么,在创建 TestNew 时就要这样 TestNew* pTestNew = new("hahaha", 1) TestNew();,在 new 后填上参数,然后就会去执行重载后的 operator new 函数
    class TestNew : public TestBase
    {
        public:
        TestNew();
        ~TestNew();
        void Test();
        void* operator new (size_t size, const char* msg, int ret);
        void operator delete(void* pointer);
    }
    
    void* TestNew::operator new (size_t size, const char* msg, int ret)
    {
        std::cout << "operator new size " << size << " with string " << msg << " ret " << ret << std::endl;
        return ::operator new(size);
    }
    
    
    void TestNew::operator delete(void* pointer)
    {
        std::cout << "operator delete" << std::endl;
        ::operator delete(pointer);
    }
    
    • 然后 operator new 有三种形式
      • 第一种,void* operator new (std::size_t size) throw (std::bad_alloc); ,默认的,成功时返回对象内存地址,失败时抛异常
      • 第二种,void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw();,失败时不抛异常
      • 第三种,void* operator new (std::size_t size, void* ptr) throw();,这个就是下面说的 placement new,这种不能重载
    • 同理,operator delete 也一样
  • placement new

    • 系统内存分配的速度其实相对来说是不快的,特别是内存中碎片很多,此时又需要申请一块大内存时,就会更慢,所以如果是需要频繁申请内存的场景下,就可以使用 placement new 了。
    • 其原理就是在已申请的内存上创建对象,因为少了申请内存的操作(因为提前申请好了),所以效率会高些。
    • 使用时,需要传入内存的首地址,Test* pTest = new (buff)Test;
    • 这个内存地址可以是堆里的,也可以是栈里的
    • 使用上面的方法创建对象时,并不会申请一块新内存,而是在传入的内存地址处创建对象,然后调用对应的构造函数,然后释放时,需要显式调用析构函数
    • 这种方式比较适合用于需要频繁申请内存对象的场合,比如接收消息,每次收到客户端发过来的消息,需要申请一块内存去处理,用完后又需要将其释放,如果使用普通的申请方式,就会导致申请次数很多,而且容易产生内存碎片,所以可以使用 placement new 的方式,将申请内存的操作省去,直接复用内存,做对象的构造和析构即可
    class Test
    {
    public:
        Test() {}
        ~Test() {}
    
        int GetId() { return m_nId; }
        void SetId(int nId) { m_nId = nId; }
    
    private:
        int m_nId;
    }
    
    void DoTest()
    {
        char* pBuff = new char[sizeof(Test)];
        Test* pTest = new (buff)Test;
        pTest->SetId(1);
        pTest->~Test();
        delete []buff;
    }