【C++內存管理】4_內存分配的重載示例

類內重載 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.pngios

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.pngthis

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。spa

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;
}

輸出:code

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

  • extra 的做用: 輔助實現引用計數(淺拷貝)
相關文章
相關標籤/搜索