欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++的new和delete詳解

 更新時(shí)間:2021年09月24日 15:57:23   作者:歐陽(yáng)大哥2013  
這篇文章主要介紹的是C++的new和delete,可以這么理解 new相當(dāng)于是malloc 、delete相當(dāng)于是free,下面我們就來詳情介紹,,需要的朋友可以參一考

1、new和delete的內(nèi)部實(shí)現(xiàn)

C++中如果要在堆內(nèi)存中創(chuàng)建和銷毀對(duì)象需要借助關(guān)鍵字newdelete來完成。比如下面的代碼

class CA
{
    public:
       CA()m_a(0){}
       CA(int a):m_a(a){}

       virtual void foo(){ cout<<m_a<<endl;}
       int m_a;
};

void main()
{
       CA *p1 = new CA;
       CA *p2 = new CA(10);
       CA *p3 = new CA[20];

       delete p1;
       delete p2;
       delete[] p3;
}

newdelete既是C++中的關(guān)鍵字也是一種特殊的運(yùn)算符。

   void* operator new(size_t size);
   void* operator new[](size_t size);
   void  operator delete(void *p);
   void  operator delete[](void *p);

newdelete不僅承載著內(nèi)存分配的功能還承載著對(duì)象構(gòu)造函數(shù)的調(diào)用功能,因此上面的對(duì)象創(chuàng)建代碼其實(shí)在編譯時(shí)會(huì)轉(zhuǎn)化為如下的實(shí)現(xiàn):

   CA *p1 = operator new(sizeof(CA));  //分配堆內(nèi)存
      CA::CA(p1);   //調(diào)用構(gòu)造函數(shù)

      CA *p2 = operator new(sizeof(CA));  //分配堆內(nèi)存
      CA::CA(p2, 10);   //調(diào)用構(gòu)造函數(shù)
     
      CA *p3 = operator new[](20 * sizeof(CA));
      CA *pt = p3;
      for (int i = 0; i < 20; i++)
     {
         CA::CA(pt);
         pt += 1;
     }

     CA::~CA(p1);
     operator delete(p1);
     
     CA::~CA(p2);
     operator delete(p2);

     CA *pt = p3;
     for (int i = 0; i < 20; i++)
     {
          CA::~CA(pt);
          pt += 1;
     }
     operator delete[](p3);

看到上面的代碼也許你會(huì)感到疑惑,怎么在編譯時(shí)怎么會(huì)在源代碼的基礎(chǔ)上插入這么多的代碼。這也是很多C程序員吐槽C++語(yǔ)言的原因:C++編譯器會(huì)偷偷插入很多未知的代碼或者對(duì)源代碼進(jìn)行修改和處理,而這些插入和修改動(dòng)作對(duì)于程序員來說是完全不可知的! 言歸正傳,我們還能從上面的代碼中看出new和delete操作其實(shí)是分別進(jìn)行了2步操作:1.內(nèi)存的分配,2.構(gòu)造函數(shù)的調(diào)用;3.析構(gòu)函數(shù)的調(diào)用,4.內(nèi)存的銷毀。所以當(dāng)對(duì)象是從堆內(nèi)存分配時(shí),構(gòu)造函數(shù)執(zhí)前內(nèi)存就已經(jīng)完成分配,同樣當(dāng)析構(gòu)函數(shù)執(zhí)行完成后內(nèi)存才會(huì)被銷毀。 這里面一個(gè)有意思的問題就是當(dāng)我們分配或者銷毀的是數(shù)組對(duì)象時(shí),系統(tǒng)又是如何知道應(yīng)該調(diào)用多少次構(gòu)造函數(shù)以及調(diào)用多少次析構(gòu)函數(shù)的呢?答案就是在內(nèi)存分配里面。當(dāng)我們調(diào)用operator new[]來分配數(shù)組對(duì)象時(shí),編譯器時(shí)系統(tǒng)內(nèi)部會(huì)增加4或者8字節(jié)的分配空間用來保存所分配的數(shù)組對(duì)象的數(shù)量。當(dāng)對(duì)數(shù)組對(duì)象調(diào)用構(gòu)造和析構(gòu)函數(shù)時(shí)就可以根據(jù)這個(gè)數(shù)量值來進(jìn)行循環(huán)處理了。因此上面對(duì)數(shù)組對(duì)象的分配和銷毀的真實(shí)代碼其實(shí)是按如下方式處理的:

  //  CA *p3 = new CA[20]; 這句代碼在編譯時(shí)其實(shí)會(huì)轉(zhuǎn)化為如下的代碼片段
     unsigned long *p = operator new[](20 * sizeof(CA) + sizeof(unsigned long));  //64位系統(tǒng)多分配8字節(jié)
     *p = 20;   //這里保存分配的對(duì)象的數(shù)量。
     CA *p3 = (CA*)(p + 1);
     CA *pt = p3;
     for (int i = 0; i < *p; i++)
     {
         CA::CA(pt);
         pt += 1;
     }


    // delete[] p3;   這句代碼在編譯時(shí)其實(shí)會(huì)轉(zhuǎn)化為如下的代碼片段
     unsigned long *p =  ((unsigned long*)p3)  - 1;
     CA *pt = p3;
     for (int i = 0; i < *p; i++)
     {
          CA::~CA(pt);
          pt += 1;
      }
      operator delete[](p);

可見C++中為我們隱藏了多少細(xì)節(jié)?。〖热?code>new和delete操作默認(rèn)是從堆中進(jìn)行內(nèi)存分配,而且newdelete又是一個(gè)普通的運(yùn)算符函數(shù),那么他內(nèi)部是如何實(shí)現(xiàn)呢?其實(shí)也很簡(jiǎn)單。我們知道C語(yǔ)言中堆內(nèi)存分配和銷毀的函數(shù)是malloc/free。因此C++中對(duì)系統(tǒng)默認(rèn)的newdelete運(yùn)算符函數(shù)就可以按如下的方法實(shí)現(xiàn):

void * operator new(size_t size)
{
     return malloc(size);
} 

void * operator new[](size_t size)
{
     return malloc(size);
}

void operator delete(void *p)
{
     free(p);
}

void operator delete[](void *p)
{
    free(p);
}

這里需要注意的是你在代碼里面使用new關(guān)鍵字和使用operator new操作符所產(chǎn)生的效果是不一樣的。如果你在代碼里面使用的是new關(guān)鍵字那么系統(tǒng)內(nèi)部除了會(huì)調(diào)用operator new操作符來分配內(nèi)存還會(huì)調(diào)用構(gòu)造函數(shù),而如果你直接使用operator new時(shí)則只會(huì)進(jìn)行內(nèi)存分配而不會(huì)執(zhí)行任何構(gòu)造就比如下面的代碼:

   CA *p1 = new CA;   //這里會(huì)分配內(nèi)存和執(zhí)行構(gòu)造函數(shù)

   CA *p2 = operator new(sizeof(CA));   //這里只是執(zhí)行了普通的堆內(nèi)存分配而不會(huì)調(diào)用構(gòu)造函數(shù)

上述的偽代碼都是在運(yùn)行時(shí)通過查看匯編語(yǔ)言而得出的結(jié)論,我是在XCODE編譯器上查看運(yùn)行的結(jié)果,有可能不同的編譯器會(huì)有一些實(shí)現(xiàn)的差異,但是不管如何要想真實(shí)的了解內(nèi)部實(shí)現(xiàn)原理還是要懂一些匯編的知識(shí)為最好。

2、placement技術(shù)

系統(tǒng)默認(rèn)的new關(guān)鍵字除了分配堆內(nèi)存外還進(jìn)行構(gòu)造函數(shù)的調(diào)用。而實(shí)際中我們可能有一些已經(jīng)預(yù)先分配好的內(nèi)存區(qū)域,我們想在這些已經(jīng)分配好的內(nèi)存中來構(gòu)建一個(gè)對(duì)象。還有一種情況是不希望進(jìn)行頻繁的堆內(nèi)存分配和釋放而只是對(duì)同一塊內(nèi)存進(jìn)行重復(fù)的對(duì)象構(gòu)建和銷毀。就如下面的代碼:

char buf1[100];
CA *p1 = (CA*)buf1;
CA::CA(p1);
p1->foo();
p1->m_a = 10;


char *buf2 = new char[sizeof(CA)];
CA *p2 = (CA*)buf2;
CA::CA(p2);
p2->foo();
p2->m_a = 20;


p1->~CA();
p2->~CA();

delete[] buf2;

可以看出代碼中buf1是棧內(nèi)存而buf2是堆內(nèi)存,這兩塊內(nèi)存區(qū)域都是已經(jīng)分配好了的內(nèi)存,現(xiàn)在我們想把這些內(nèi)存來當(dāng)做CA類的對(duì)象來使用,因此我們需要對(duì)內(nèi)存調(diào)用類的構(gòu)造函數(shù)CA::CA()才可以,構(gòu)造函數(shù)的內(nèi)部實(shí)現(xiàn)會(huì)為內(nèi)存區(qū)域填充虛表指針,這樣對(duì)象才可以調(diào)用諸如foo虛函數(shù)。但是這樣寫代碼不夠優(yōu)雅,那么有沒有比較優(yōu)雅的方法來實(shí)現(xiàn)在一塊已經(jīng)存在的內(nèi)存上來構(gòu)建對(duì)象呢? 答案就是 placement技術(shù)。 C++中的仍然是使用newdelete來實(shí)現(xiàn)這種技術(shù)。newdelete除了實(shí)現(xiàn)默認(rèn)的操作符外還重載實(shí)現(xiàn)了如下的操作符函數(shù):

void* operator new(size_t  size, void *p)
{
   return p;
}

void* operator new[](size_t size, void *p)
{
   return p;
}

void operator delete(void *p1, void *p2)
{
   // do nothing..
}

void operator delete[](void *p1, void *p2)
{
   // do nothing..
}

我們稱這四個(gè)運(yùn)算符為 placement new placement delete 。通過這幾個(gè)運(yùn)算符我們就可以優(yōu)雅的實(shí)現(xiàn)上述的功能:

char buf1[100];
CA *p1 = new(buf1) CA(10);   //調(diào)用 operator new(size_t, void*)
p1->foo();


char *buf2 = new char[sizeof(CA)];
CA *p2 = new(buf2) CA(20);     //調(diào)用 operator new(size_t, void*)
p2->foo();


p1->~CA();
operator delete(p1, buf1);  //調(diào)用 operator delete(void*, void*)

p2->~CA();
operator delete(p2, buf2);  //調(diào)用 operator delete(void*, void*)

delete[] buf2;

上面的例子里面發(fā)現(xiàn)通過placement new可以很優(yōu)雅的在現(xiàn)有的內(nèi)存中構(gòu)建對(duì)象,而析構(gòu)時(shí)不能直接調(diào)用delete p1, delete p2來銷毀對(duì)象,必須人為的調(diào)用析構(gòu)函數(shù)以及placement delete 函數(shù)。并且從上面的placement delete的實(shí)現(xiàn)來看里面并沒有任何代碼,既然如此為什么還要定義一個(gè)placement delete呢? 答案就是C++中的規(guī)定對(duì)new和delete的運(yùn)算符重載必須是要成對(duì)實(shí)現(xiàn)的。而且前面曾經(jīng)說過對(duì)delete的使用如果帶了operator前綴時(shí)就只是一個(gè)普通的函數(shù)調(diào)用。因此為了完成析構(gòu)以及和new操作符的匹配,就必須要人為的調(diào)用對(duì)象的析構(gòu)函數(shù)以及placement delete函數(shù)。 除了上面舉的例子外placement技術(shù)的使用還可以減少內(nèi)存的頻繁分配以及提升系統(tǒng)的性能。

void main()
{
      for (int i = 0; i < 10000; i++)
      {
           CA *p = new CA(i);
           p->foo();
           delete p;
      }
}

例子里面循環(huán)10000次,每次循環(huán)都創(chuàng)建一個(gè)堆內(nèi)存對(duì)象,然后調(diào)用虛函數(shù)foo后再進(jìn)行銷毀。最終的結(jié)果是程序運(yùn)行時(shí)會(huì)進(jìn)行10000次的頻繁的堆內(nèi)存分配和銷毀。很明顯這是有可能會(huì)影響系統(tǒng)性能的而且還有可能發(fā)生堆內(nèi)存分配失敗的情況。而如果我們借助placement 技術(shù)就可以很簡(jiǎn)單的解決這些問題。

void main()
{
      char *buf = new[](sizeof(CA));
      for (int i = 0; i < 10000; i++)
      {
            CA *p = new(buf) CA(i);
            p->foo();
            p->~CA();
            operator delete(p, buf);
      }
      delete[] buf;
}

上面的例子里面只進(jìn)行了一次堆內(nèi)存分配,在循環(huán)里面都是借助已經(jīng)存在的內(nèi)存來構(gòu)建對(duì)象,不會(huì)再分配內(nèi)存了。這樣對(duì)內(nèi)存的重復(fù)利用就使得程序的性能得到非常大的提升。

3、new和delete運(yùn)算符重載

發(fā)現(xiàn)一個(gè)很有意思的事情就是越高級(jí)的語(yǔ)言就越會(huì)將一些系統(tǒng)底層的東西進(jìn)行封裝并形成一個(gè)語(yǔ)言級(jí)別的關(guān)鍵字來使用。比如C++中的new和delete是用于構(gòu)建和釋放堆內(nèi)存對(duì)象的關(guān)鍵字,又比如go語(yǔ)言中chan關(guān)鍵字是用于進(jìn)行同步或者異步的隊(duì)列數(shù)據(jù)傳輸通道。 C++語(yǔ)言內(nèi)置默認(rèn)實(shí)現(xiàn)了一套全局newdelete的運(yùn)算符函數(shù)以及placement new/delete運(yùn)算符函數(shù)。不管是類還是內(nèi)置類型都可以通過new/delete來進(jìn)行堆內(nèi)存對(duì)象的分配和釋放的。對(duì)于一個(gè)類來說,當(dāng)我們使用new來進(jìn)行構(gòu)建對(duì)象時(shí),首先會(huì)檢查這個(gè)類是否重載了new運(yùn)算符,如果這個(gè)類重載了new運(yùn)算符那么就會(huì)調(diào)用類提供的new運(yùn)算符來進(jìn)行內(nèi)存分配,而如果沒有提供new運(yùn)算符時(shí)就使用系統(tǒng)提供的全局new運(yùn)算符來進(jìn)行內(nèi)存分配。內(nèi)置類型則總是使用系統(tǒng)提供的全局new運(yùn)算符來進(jìn)行內(nèi)存的分配。對(duì)象的內(nèi)存銷毀流程也是和分配一致的。 new和delete運(yùn)算符既支持全局的重載又支持類級(jí)別的函數(shù)重載。下面是這種運(yùn)算符的定義的格式:

//全局運(yùn)算符定義格式
void * operator new(size_t size [, param1, param2,....]);
void operator delete(void *p [, param1, param2, ...]);

//類內(nèi)運(yùn)算符定義格式
class CA
{
  void * operator new(size_t size [, param1, param2,....]);
  void operator delete(void *p [, param1, param2, ...]);
};

對(duì)于new/delete運(yùn)算符重載我們總有如何下規(guī)則:

  • newdelete運(yùn)算符重載必須成對(duì)出現(xiàn)
  • new運(yùn)算符的第一個(gè)參數(shù)必須是size_t類型的,也就是指定分配內(nèi)存的size尺寸;delete運(yùn)算符的第一個(gè)參數(shù)必須是要銷毀釋放的內(nèi)存對(duì)象。其他參數(shù)可以任意定義。
  • 系統(tǒng)默認(rèn)實(shí)現(xiàn)了new/delete、new[]/delete[] 、 placement new / delete 6個(gè)運(yùn)算符函數(shù)。它們都有特定的意義。
  • 你可以重寫默認(rèn)實(shí)現(xiàn)的全局運(yùn)算符,比如你想對(duì)內(nèi)存的分配策略進(jìn)行自定義管理或者你想監(jiān)測(cè)堆內(nèi)存的分配情況或者你想做堆內(nèi)存的內(nèi)存泄露監(jiān)控等。但是你重寫的全局運(yùn)算符一定要滿足默認(rèn)的規(guī)則定義。
  • 如果你想對(duì)某個(gè)類的堆內(nèi)存分配的對(duì)象做特殊處理,那么你可以重載這個(gè)類的new/delete運(yùn)算符。當(dāng)重載這兩個(gè)運(yùn)算符時(shí)雖然沒有帶static屬性,但是不管如何對(duì)類的new/delete運(yùn)算符的重載總是被認(rèn)為是靜態(tài)成員函數(shù)。
  • 當(dāng)delete運(yùn)算符的參數(shù)>=2個(gè)時(shí),就需要自己負(fù)責(zé)對(duì)象析構(gòu)函數(shù)的調(diào)用,并且以運(yùn)算符函數(shù)的形式來調(diào)用delete運(yùn)算符。

一般情況下你不需要對(duì)new/delete運(yùn)算符進(jìn)行重載,除非你的整個(gè)應(yīng)用或者某個(gè)類有特殊的需求時(shí)才會(huì)如此。下面的例子你可以看到我的各種運(yùn)算符的重載方法以及使用方法:

//CA.h

class CA
{
public:
    //類成員函數(shù)
    void * operator new(size_t size);
    void * operator new[](size_t size);
    void * operator new(size_t size, void *p);
    void * operator new(size_t size, int a, int b);
    
    void operator delete(void *p);
    void operator delete[](void *p);
    void operator delete(void *p, void *p1);
    void operator delete(void *p, int a, int b);
};

class CB
{
public:
    CB(){}
};


//全局運(yùn)算符函數(shù),請(qǐng)謹(jǐn)慎重寫覆蓋全局運(yùn)算符函數(shù)。
void * operator new(size_t size);
void * operator new[](size_t size);
void * operator new(size_t size, void *p) noexcept;
void * operator new(size_t size, int a, int b);

void operator delete(void *p);
void operator delete[](void *p);
void operator delete(void *p, void *p1);
void operator delete(void *p, int a, int b);

.......................................................
//CA.cpp


void * CA::operator new(size_t size)
{
    return malloc(size);
}

void * CA::operator new[](size_t size)
{
    return malloc(size);
}

void * CA::operator new(size_t size, void *p)
{
    return p;
}

void* CA::operator new(size_t size, int a, int b)
{
    return malloc(size);
}

void CA::operator delete(void *p)
{
    free(p);
}

void CA::operator delete[](void *p)
{
    free(p);
}

void CA::operator delete(void *p, void *p1)
{
    
}

void CA::operator delete(void *p, int a, int b)
{
    free(p);
}


void * operator new(size_t size)
{
    return  malloc(size);
}

void * operator new[](size_t size)
{
    return malloc(size);
}

void * operator new(size_t size, void *p) noexcept
{
    return p;
}

void* operator new(size_t size, int a, int b)
{
    return malloc(size);
}

void operator delete(void *p)
{
    free(p);
}

void operator delete[](void *p)
{
    free(p);
}

void operator delete(void *p, void *p1)
{
    
}

void operator delete(void *p, int a, int b)
{
    free(p);
}

..................................
//main.cpp

int main(int argc, const char * argv[]) {
    
    char buf[100];

    CA *a1 = new CA();   //調(diào)用void * CA::operator new(size_t size)
    
    CA *a2 = new CA[10];  //調(diào)用void * CA::operator new[](size_t size)
    
    CA *a3 = new(buf)CA();  //調(diào)用void * CA::operator new(size_t size, void *p)
    
    CA *a4 = new(10, 20)CA();  //調(diào)用void* CA::operator new(size_t size, int a, int b)
    
    
    delete a1;  //調(diào)用void CA::operator delete(void *p)
    
    delete[] a2;  //調(diào)用void CA::operator delete[](void *p)
    
    //a3用的是placement new的方式分配,因此需要自己調(diào)用對(duì)象的析構(gòu)函數(shù)。
    a3->~CA();
    CA::operator delete(a3, buf);  //調(diào)用void CA::operator delete(void *p, void *p1),記得要帶上類命名空間。

    //a4的運(yùn)算符參數(shù)大于等于2個(gè)所以需要自己調(diào)用對(duì)象的析構(gòu)函數(shù)。
    a4->~CA();
    CA::operator delete(a4, 10, 20); //調(diào)用void CA::operator delete(void *p, int a, int b)
    
    //CB類沒有重載運(yùn)算符,因此使用的是全局重載的運(yùn)算符。
    
    CB *b1 = new CB();  //調(diào)用void * operator new(size_t size)
 
    
    CB *b2 = new CB[10]; //調(diào)用void * operator new[](size_t size)
    
    //這里你可以看到同一塊內(nèi)存可以用來構(gòu)建CA類的對(duì)象也可以用來構(gòu)建CB類的對(duì)象
    CB *b3 = new(buf)CB();  //調(diào)用void * operator new(size_t size, void *p)
    
    CB *b4 = new(10, 20)CB(); //調(diào)用void* operator new(size_t size, int a, int b)
    

    delete b1;  //調(diào)用void operator delete(void *p)

    
    delete[] b2;   //調(diào)用void operator delete[](void *p)
    
    
    //b3用的是placement new的方式分配,因此需要自己調(diào)用對(duì)象的析構(gòu)函數(shù)。
    b3->~CB();
    ::operator delete(b3, buf);  //調(diào)用void operator delete(void *p, void *p1)
    
    //b4的運(yùn)算符參數(shù)大于等于2個(gè)所以需要自己調(diào)用對(duì)象的析構(gòu)函數(shù)。
    b4->~CB();
    ::operator delete(b4, 10, 20);  //調(diào)用void operator delete(void *p, int a, int b)
   
   return 0;
} 

我是在XCODE上測(cè)試上面的代碼的,因?yàn)橹貙懥巳值?code>new/delete運(yùn)算符,并且內(nèi)部是通過malloc來實(shí)現(xiàn)堆內(nèi)存分配的, malloc函數(shù)申明了不能返回NULL的返回結(jié)果檢測(cè): void *malloc(size_t __size) __result_use_check __alloc_size(1) ; 因此有可能你在測(cè)試時(shí)會(huì)發(fā)生崩潰的問題。如果出現(xiàn)這個(gè)問題你可以嘗試著注釋掉對(duì)全局new/delete重寫的代碼,再運(yùn)行查看結(jié)果。 可見如果你嘗試著覆蓋重寫全局的new/delete時(shí)是有可能產(chǎn)生風(fēng)險(xiǎn)的。

4、對(duì)象的自動(dòng)刪除技術(shù)

一般來說系統(tǒng)對(duì)new/delete的默認(rèn)實(shí)現(xiàn)就能滿足我們的需求,我們不需要再去重載這兩個(gè)運(yùn)算符。那為什么C++還提供對(duì)這兩個(gè)運(yùn)算符的重載支持呢?答案還是在運(yùn)算符本身具有的缺陷所致。我們知道用new關(guān)鍵字來創(chuàng)建堆內(nèi)存對(duì)象是分為了2步:1.是堆內(nèi)存分配,2.是對(duì)象構(gòu)造函數(shù)的調(diào)用。而這兩步中的任何一步都有可能會(huì)產(chǎn)生異常。如果說是在第一步出現(xiàn)了問題導(dǎo)致內(nèi)存分配失敗則不會(huì)調(diào)用構(gòu)造函數(shù),這是沒有問題的。如果說是在第二步構(gòu)造函數(shù)執(zhí)行過程中出現(xiàn)了異常而導(dǎo)致無法正常構(gòu)造完成,那么就應(yīng)該要將第一步中所分配的堆內(nèi)存進(jìn)行銷毀。C++中規(guī)定如果一個(gè)對(duì)象無法完全構(gòu)造那么這個(gè)對(duì)象將是一個(gè)無效對(duì)象,也不會(huì)調(diào)用析構(gòu)函數(shù)。為了保證對(duì)象的完整性,當(dāng)通過new分配的堆內(nèi)存對(duì)象在構(gòu)造函數(shù)執(zhí)行過程中出現(xiàn)異常時(shí)就會(huì)停止構(gòu)造函數(shù)的執(zhí)行并且自動(dòng)調(diào)用對(duì)應(yīng)的delete運(yùn)算符來對(duì)已經(jīng)分配的堆內(nèi)存執(zhí)行銷毀處理,這就是所謂的對(duì)象的自動(dòng)刪除技術(shù)。正是因?yàn)橛辛藢?duì)象的自動(dòng)刪除技術(shù)才能解決對(duì)象構(gòu)造不完整時(shí)會(huì)造成內(nèi)存泄露的問題。

當(dāng)對(duì)象構(gòu)造過程中拋出異常時(shí),C++的異常處理機(jī)制會(huì)在特定的地方插入代碼來實(shí)現(xiàn)對(duì)對(duì)象的delete運(yùn)算符的調(diào)用,如果想要具體了解情況請(qǐng)參考C++對(duì)異常處理實(shí)現(xiàn)的相關(guān)知識(shí)點(diǎn)。

全局delete運(yùn)算符函數(shù)所支持的對(duì)象的自動(dòng)刪除技術(shù)雖然能解決對(duì)象本身的內(nèi)存泄露問題,但是卻不能解決對(duì)象構(gòu)造函數(shù)內(nèi)部的數(shù)據(jù)成員的內(nèi)存分配泄露問題,我們來看下面的代碼:

class CA
{
  public:
    CA()
    {
          m_pa  = new int;
          throw 1;
    }

  ~CA()
   {
         delete m_pa;
         m_pa = NULL;
   }

 private:
      int *m_pa;
};

void main()
{
     try
     {
           CA *p = new CA();
           delete p;  //這句代碼永遠(yuǎn)不會(huì)執(zhí)行
     }
     catch(int)
    {
          cout << "oops!" << endl;
    }
}

上面的代碼中可以看到類CA中的對(duì)象在構(gòu)造函數(shù)內(nèi)部拋出了異常,雖然系統(tǒng)會(huì)對(duì)p對(duì)象執(zhí)行自動(dòng)刪除技術(shù)來銷毀分配好的內(nèi)存,但是對(duì)于其內(nèi)部的數(shù)據(jù)成員m_pa來說,因?yàn)闃?gòu)造不完整就不會(huì)調(diào)用析構(gòu)函數(shù)來銷毀分配的堆內(nèi)存,這樣就導(dǎo)致了m_pa這塊內(nèi)存出現(xiàn)了泄露。怎么解決這類問題呢? 答案你是否想到了? 那就是重載CA類的new/delete運(yùn)算符。我們來看通過對(duì)CA重載運(yùn)算符解決問題的代碼:

class CA
{
public:
    CA(){
        m_pa = new int;
        throw 1;
    }
    //因?yàn)閷?duì)象構(gòu)造未完成所以析構(gòu)函數(shù)永遠(yuǎn)不會(huì)被調(diào)用
    ~CA()
    {
        delete m_pa;
        m_pa = NULL;
    }
    
    void * operator new(size_t size)
    {
        return malloc(size);
    }
    //重載delete運(yùn)算符,把已經(jīng)分配的內(nèi)存銷毀掉。
    void operator delete(void *p)
    {
        CA *pb = (CA*)p;
        if (pb->m_pa != NULL)
            delete pb->m_pa;
        
        free(p);
    }
    
private:
    int *m_pa;
};

因?yàn)镃++對(duì)自動(dòng)刪除技術(shù)的支持,當(dāng)CA對(duì)象在構(gòu)造過程中發(fā)生異常時(shí),我們就可以通過重載delete運(yùn)算符來解決那些在構(gòu)造函數(shù)中分配的數(shù)據(jù)成員內(nèi)存但又不會(huì)調(diào)用析構(gòu)函數(shù)來銷毀的數(shù)據(jù)成員的內(nèi)存問題。這我想就是為什么C++中要支持對(duì)new/delete運(yùn)算符在類中重載的原因吧。

到此這篇關(guān)于C++的new和delete詳解的文章就介紹到這了,更多相關(guān)C++的new和delete內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語(yǔ)言數(shù)組應(yīng)用實(shí)現(xiàn)掃雷游戲

    C語(yǔ)言數(shù)組應(yīng)用實(shí)現(xiàn)掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言數(shù)組應(yīng)用實(shí)現(xiàn)掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Qt+OpenCV利用幀差法實(shí)現(xiàn)車輛識(shí)別

    Qt+OpenCV利用幀差法實(shí)現(xiàn)車輛識(shí)別

    所謂幀差法也就是對(duì)連續(xù)圖像幀做差分運(yùn)算,其結(jié)果與定義好的閾值比較,若大于閾值則為運(yùn)動(dòng)目標(biāo)值為1,否則值為0?。本文將利用幀差法實(shí)現(xiàn)車輛識(shí)別,感興趣的可以了解一下
    2022-08-08
  • 關(guān)于define與C 的內(nèi)存

    關(guān)于define與C 的內(nèi)存

    本文主要介紹了C語(yǔ)言中#define到底存在程序的哪個(gè)區(qū),以及工作流程和效率與普通函數(shù)的區(qū)別,希望能幫助需要的小伙伴
    2016-07-07
  • C語(yǔ)言自定義類型詳解(結(jié)構(gòu)體、枚舉、聯(lián)合體和位段)

    C語(yǔ)言自定義類型詳解(結(jié)構(gòu)體、枚舉、聯(lián)合體和位段)

    這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中結(jié)構(gòu)體、枚舉、聯(lián)合體和位段自定義類型的相關(guān)資料,分別介紹了結(jié)構(gòu)體、枚舉、聯(lián)合體和位段等四種自定義類型,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • C語(yǔ)言基礎(chǔ)指針詳解教程

    C語(yǔ)言基礎(chǔ)指針詳解教程

    此處對(duì)于指針做一些簡(jiǎn)要的介紹,作者實(shí)屬初學(xué),寫博客也是作者學(xué)習(xí)的一個(gè)過程,難免文章中有內(nèi)容理解不到位或者有不當(dāng)之處,還請(qǐng)朋友們不吝指正,希望大家給予支持
    2021-11-11
  • 利用Matlab繪制甘特圖的方法詳解

    利用Matlab繪制甘特圖的方法詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Matlab實(shí)現(xiàn)甘特圖(gantt?chart)的繪制,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下
    2022-10-10
  • 詳解C++中的inline用法

    詳解C++中的inline用法

    在c/c++中,為了解決一些頻繁調(diào)用的小函數(shù)大量消耗??臻g(棧內(nèi)存)的問題,特別的引入了inline修飾符,表示為內(nèi)聯(lián)函數(shù)。 ??臻g就是指放置程序的局部數(shù)據(jù)(也就是函數(shù)內(nèi)數(shù)據(jù))的內(nèi)存空間
    2017-09-09
  • C++實(shí)現(xiàn)日期類的方法詳解

    C++實(shí)現(xiàn)日期類的方法詳解

    這篇文章主要給大家介紹了C++實(shí)現(xiàn)日期類的方法,文中通過代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-01-01
  • C語(yǔ)言實(shí)現(xiàn)點(diǎn)菜系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)點(diǎn)菜系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)點(diǎn)菜系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • c語(yǔ)言?數(shù)據(jù)存儲(chǔ)與原碼?反碼?補(bǔ)碼詳細(xì)解析

    c語(yǔ)言?數(shù)據(jù)存儲(chǔ)與原碼?反碼?補(bǔ)碼詳細(xì)解析

    不知道你是否和我一樣好奇,學(xué)習(xí)編程語(yǔ)言的同時(shí)想,各個(gè)數(shù)據(jù)類型是怎樣在我們的內(nèi)存中儲(chǔ)存的呢,如果你仔細(xì)深入了解的話,你會(huì)了解其中的樂趣,了解科學(xué)家們的偉大,了解c語(yǔ)言
    2022-02-02

最新評(píng)論