类内重载 operator new/operator delete 示例

#include <iostream>
#include <string>

using namespace std;

class Foo {
public:
    int _id;
    int _data;
    int _num;

public:
    // 如果没有重载的成员函数则调用全局版本
    static void *operator new(size_t size);
    static void operator delete(void *pdead, size_t size);
    static void *operator new[](size_t size);
    static void operator delete[](void *pdead, size_t size);

    Foo() : _id(0) {
        cout << "default ctor.this=" << this << " id=" << _id << endl;
    }

    Foo(int i) : _id(i) {
        cout << "ctor. this=" << this << " id=" << _id << endl;
    }

    // virtual
    ~Foo() {
        cout << "dtor. this=" << this << " id=" << _id << endl;
    }
};

void *Foo::operator new(size_t size)
{
    Foo *p = (Foo*)malloc(size);

    cout << "Foo::operator new(), size=" << size << "\t return: " << p << endl;

    return p;
}

void Foo::operator delete(void *pdead, size_t size)
{
    cout << "Foo::operator delete(), pdead= " << pdead << " size= " << size << endl;

    free(pdead);
}

void *Foo::operator new[](size_t size)
{
    Foo *p = (Foo*)malloc(size);

    cout << "Foo::operator new[](), size=" << size << "\t return: " << p << endl;

    return p;
}
void Foo::operator delete[](void *pdead, size_t size)
{
    cout << "Foo::operator delete[](), pdead= " << pdead << " size= " << size << endl;

    free(pdead);
}

int main()
{
    cout << "sizeof(Foo)=" << sizeof(Foo) << endl;

    cout << "============" << endl;

    Foo *p = new Foo(7);
    delete p;

    cout << "============" << endl;

    Foo* pArray = new Foo[5];
    delete [] pArray;

    return 0;
}

当 Foo 无虚析构函数时,输出:
image.png

sizeof(Foo)=12  // 注意这里 !!
============
Foo::operator new(), size=12     return: 0x7617b0
ctor. this=0x7617b0 id=7
dtor. this=0x7617b0 id=7
Foo::operator delete(), pdead= 0x7617b0 size= 12
============
Foo::operator new[](), size=68     return: 0x7617b0
default ctor.this=0x7617b8 id=0
default ctor.this=0x7617c4 id=0
default ctor.this=0x7617d0 id=0
default ctor.this=0x7617dc id=0
default ctor.this=0x7617e8 id=0
dtor. this=0x7617e8 id=0
dtor. this=0x7617dc id=0
dtor. this=0x7617d0 id=0
dtor. this=0x7617c4 id=0
dtor. this=0x7617b8 id=0
Foo::operator delete[](), pdead= 0x7617b0 size= 68

当 Foo 有虚析构函数时,输出:
image.png

sizeof(Foo)=16  // 注意这里 !!
============
Foo::operator new(), size=16     return: 0xfb8088
ctor. this=0xfb8088 id=7
dtor. this=0xfb8088 id=7
Foo::operator delete(), pdead= 0xfb8088 size= 16
============
Foo::operator new[](), size=84     return: 0xfb8088
default ctor.this=0xfb808c id=0
default ctor.this=0xfb809c id=0
default ctor.this=0xfb80ac id=0
default ctor.this=0xfb80bc id=0
default ctor.this=0xfb80cc id=0
dtor. this=0xfb80cc id=0
dtor. this=0xfb80bc id=0
dtor. this=0xfb80ac id=0
dtor. this=0xfb809c id=0
dtor. this=0xfb808c id=0
Foo::operator delete[](), pdead= 0xfb8088 size= 84
  • 当类中带有虚函数时,会多占用 4 字节内存空间。(为虚函数表指针,以实现多态)
int main()
{
    cout << "sizeof(Foo)=" << sizeof(Foo) << endl;

    cout << "============" << endl;

    Foo *p = ::new Foo(7);        // 注意这里 !!
    ::delete p;

    cout << "============" << endl;

    Foo* pArray = ::new Foo[5];   // 注意这里 !!
    ::delete[] pArray;

    return 0;
}

当 Foo 无虚析构函数时,输出[类中重载版本 new / delete 未被调用]:
image.png

sizeof(Foo)=12
============
ctor. this=0x7617b0 id=7
dtor. this=0x7617b0 id=7
============
default ctor.this=0x7617b8 id=0
default ctor.this=0x7617c4 id=0
default ctor.this=0x7617d0 id=0
default ctor.this=0x7617dc id=0
default ctor.this=0x7617e8 id=0
dtor. this=0x7617e8 id=0
dtor. this=0x7617dc id=0
dtor. this=0x7617d0 id=0
dtor. this=0x7617c4 id=0
dtor. this=0x7617b8 id=0

当 Foo 有虚析构函数时,输出[类中重载版本 new / delete 未被调用]:
image.png

sizeof(Foo)=16
============
ctor. this=0xfb8088 id=7
dtor. this=0xfb8088 id=7
============
default ctor.this=0xfb808c id=0
default ctor.this=0xfb809c id=0
default ctor.this=0xfb80ac id=0
default ctor.this=0xfb80bc id=0
default ctor.this=0xfb80cc id=0
dtor. this=0xfb80cc id=0
dtor. this=0xfb80bc id=0
dtor. this=0xfb80ac id=0
dtor. this=0xfb809c id=0
dtor. this=0xfb808c id=0
  • 如上调用(也就是写上 global scope operator ::), 会绕过前述所有 overloaded functions, 强迫使用 global version

重载 new() / delete()

我们可以重载 class member operator new(), 写出多个版本,前提是每一个版本的声明都必须具有独特的参数列表,其中第一个参数必须是 size_t, 其余参数以 new 所指定的 placement arguments 为初值。 出现于 new(...) 小括号内的便是所谓的 placement argument。

Foo *pf = new (300, 'c')Foo;

我们也可以重载 class member operator delete(), 写出多个版本。但它们绝不会被 delete 调用【void operator delete(void*, size_t) 版本被调用】。只有当 new 所调用的构造函数抛出异常,才会调用这些重载版本对应的 operator delete()。它只可能这样被调用,主要用来归还未能完全创建成功的对象所占用的内存空间。

#include <iostream>

using namespace std;

class Bad {
};

class Foo {
public:
    Foo() {
        cout << "Foo::Foo()" << endl;
    }

    Foo(int) {
        cout << "Foo::Foo(int)" << endl;
        throw Bad();  // 故意在这里抛出异常,测试 placement operator delete
    }

    // (1) 一般的 operator new() 重载函数
    void *operator new(size_t size) {
        cout << "operator new(size_t size), size= " << size << endl;
        return malloc(size);
    }

    // (2) 标准库已经提供的 placement new() 的重载形式
    //     (这里模拟 standard placement new 的动作, just return ptr)
    void *operator new(size_t size, void *start) {
        cout << "operator new(size_t size, void *start), size= " << size << endl;
        return start;
    }

    // (3) 一个崭新的 placement new
    void *operator new(size_t size, long extra) {
        cout << "operator new(size_t size, long extra), size= " << size << ' ' << extra << endl;
        return malloc(size + extra);
    }

    // (4) 又一个 placement new
    void *operator new(size_t size, long extra, char init) {
        cout << "operator new(size_t size, long extra, char init), size= " << size << ' ' << extra << ' ' << init << endl;
        return malloc(size + extra);
    }

    // (5) 这又是一个 placement new, 但故意写错第一参数 type (它必须是 size_t 以满足正常的 operator new)
//!    void *operator new(long extra, char init) {  // error: 'operator new' takes type size_t ('unsigned int') as first parameter
//!        cout << "op-new(long, char)" << endl;
//!        return malloc(extra);
//!    }

    // 以下是搭配上述 placement new 的各个 called placement delete.
    // 当构造函数抛出异常,这里对应的 operator (placement) delete 就会调用.
    // 应该是要负责释放其搭档兄弟 (placement new) 分配所得的内存.
    // (1) 这个就是一般的 operator delete() 的重载
    void operator delete(void* ptr, size_t) {
        cout << "operator delete(void*, size_t)" << endl;
        free(ptr);
    }

    // (2) 这个是对应上述的 (2)
    void operator delete(void *ptr, void*) {
        cout << "operator delete(void *ptr, void*)" << endl;
        free(ptr);
    }

    // (3) 这个是对应上述的 (3)
    void operator delete(void *ptr, long) {
        cout << "operator delete(void *ptr, long)" << endl;
        free(ptr);
    }

    // (4) 这个是对应上述的 (4)
    void operator delete(void *ptr, long, char) {
        cout << "operator delete(void *ptr, long, char)" << endl;
        free(ptr);
    }

private:
    int m_i;
};

int main()
{
    Foo start;

    Foo *p1 = new Foo;           // op-new(size_t)
    Foo *p2 = new (&start)Foo;   // op-new(size_t, void*)
    Foo *p3 = new (100)Foo;      // op-new(size_t, long)
    Foo *p4 = new (100, 'a')Foo; // op-new(size_t, long, char)

    delete p1;    // 注意这里!!
    delete p2;    // 注意这里!!
    delete p3;    // 注意这里!!
    delete p4;    // 注意这里!!

    return 0;
}

输出:

Foo::Foo()
operator new(size_t size), size= 4
Foo::Foo()
operator new(size_t size, void *start), size= 4
Foo::Foo()
operator new(size_t size, long extra), size= 4 100
Foo::Foo()
operator new(size_t size, long extra, char init), size= 4 100 a
Foo::Foo()
operator delete(void*, size_t)
operator delete(void*, size_t)
operator delete(void*, size_t)
operator delete(void*, size_t)
int main()
{
    Foo start;

    Foo *p5 = new (100)Foo(1);      // op-new(size_t, long) op-del(void*, long)
    Foo *p6 = new (100, 'a')Foo(1); //
    Foo *p7 = new (&start)Foo(1);   //
    Foo *p8 = new Foo(1);           //

    return 0;
}

输出:

Foo::Foo()
operator new(size_t size, long extra), size= 4 100
Foo::Foo(int)
terminate called after throwing an instance of 'Bad'  // 构造函数抛出异常
说明
以上使用 mingw81 测试,operator delete(void *, long) 并未被调用;但G2.9确实有调用。

basic_string 使用 new (extra) 扩充申请量

image.png

  • rep 的作用: 辅助实现引用计数(浅拷贝)【无声无息扩充内存】

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧