日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android jni new/delete 和 new[]/delete[]

發布時間:2024/4/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android jni new/delete 和 new[]/delete[] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天,簡單講講android里再jni使用new時如何釋放內存。


這個其實是和C++有關的知識,不過jni編程時還是需要注意的。所以這里記錄一下。


new 和 delete 到底是什么?

如果找工作的同學看一些面試的書,我相信都會遇到這樣的題:sizeof 不是函數,然后舉出一堆的理由來證明 sizeof 不是函數。在這里,和 sizeof 類似,new 和 delete 也不是函數,它們都是 C++ 定義的關鍵字,通過特定的語法可以組成表達式。和 sizeof 不同的是,sizeof 在編譯時候就可以確定其返回值,new 和 delete 背后的機制則比較復雜。
繼續往下之前,請你想想你認為 new 應該要做些什么?也許你第一反應是,new 不就和 C 語言中的 malloc 函數一樣嘛,就用來動態申請空間的。你答對了一半,看看下面語句:

string *ps = new string("hello world");

你就可以看出 new 和 malloc 還是有點不同的,malloc 申請完空間之后不會對內存進行必要的初始化,而 new 可以。所以?new?expression?背后要做的事情不是你想象的那么簡單。在我用實例來解釋 new 背后的機制之前,你需要知道?operator new?和?operator delete?是什么玩意。

operator new 和 operator delete

這兩個其實是 C++ 語言標準庫的庫函數,原型分別如下:

void *operator new(size_t); //allocate an object void *operator delete(void *); //free an objectvoid *operator new[](size_t); //allocate an array void *operator delete[](void *); //free an array

后面兩個你可以先不看,后面再介紹。前面兩個均是 C++ 標準庫函數,你可能會覺得這是函數嗎?請不要懷疑,這就是函數!C++ Primer?一書上說這不是重載 new 和 delete 表達式(如?operator=?就是重載?=?操作符),因為 new 和 delete 是不允許重載的。但我還沒搞清楚為什么要用 operator new 和 operator delete 來命名,比較費解。我們只要知道它們的意思就可以了,這兩個函數和 C 語言中的 malloc 和 free 函數有點像了,都是用來申請和釋放內存的,并且 operator new 申請內存之后不對內存進行初始化,直接返回申請內存的指針。

我們可以直接在我們的程序中使用這幾個函數。

new 和 delete 背后機制

知道上面兩個函數之后,我們用一個實例來解釋 new 和 delete 背后的機制:

我們不用簡單的 C++ 內置類型來舉例,使用復雜一點的類類型,定義一個類 A:

class A { public:A(int v) : var(v){fopen_s(&file, "test", "r");}~A(){fclose(file);}private:int var;FILE *file; };

很簡單,類 A 中有兩個私有成員,有一個構造函數和一個析構函數,構造函數中初始化私有變量 var 以及打開一個文件,析構函數關閉打開的文件。

我們使用

class *pA = new A(10);

來創建一個類的對象,返回其指針 pA。如下圖所示 new 背后完成的工作:


簡單總結一下:

  • 首先需要調用上面提到的 operator new 標準庫函數,傳入的參數為 class A 的大小,這里為 8 個字節,至于為什么是 8 個字節,你可以看看《深入 C++ 對象模型》一書,這里不做多解釋。這樣函數返回的是分配內存的起始地址,這里假設是 0x007da290。
  • 上面分配的內存是未初始化的,也是未類型化的,第二步就在這一塊原始的內存上對類對象進行初始化,調用的是相應的構造函數,這里是調用?A:A(10);?這個函數,從圖中也可以看到對這塊申請的內存進行了初始化,var=10, file 指向打開的文件。
  • 最后一步就是返回新分配并構造好的對象的指針,這里 pA 就指向 0x007da290 這塊內存,pA 的類型為類 A 對象的指針。
  • 所有這三步,你都可以通過反匯編找到相應的匯編代碼,在這里我就不列出了。

    好了,那么 delete 都干了什么呢?還是接著上面的例子,如果這時想釋放掉申請的類的對象怎么辦?當然我們可以使用下面的語句來完成:

    delete pA;

    delete 所做的事情如下圖所示:



    delete 就做了兩件事情:

  • 調用 pA 指向對象的析構函數,對打開的文件進行關閉。
  • 通過上面提到的標準庫函數 operator delete 來釋放該對象的內存,傳入函數的參數為 pA 的值,也就是 0x007d290。
  • 好了,解釋完了 new 和 delete 背后所做的事情了,是不是覺得也很簡單?不就多了一個構造函數和析構函數的調用嘛。


    這里我簡單講講,new其實是創建一個內存空間,然后自動初始化變量。delete其實就是銷毀創建的空間,然后刪除變量。


    二、不要混用 new/delete 和 new[]/delete[]

    在默認情況下,也就是不存在 operator new 的重載時,new一個自定義類型 ClassA 的對象時,C++ 會先調用 malloc 來申請一塊 sizeof(ClassA) 大小的內存(操作系統會記錄這塊內存的首地址與大小),然后調用 ClassA 的構造函數在這塊內存上初始化對象。此時,new 關鍵字會返回 malloc 得到的地址。調用delete時,會首先執行 ClassA 的析構函數,再調用 free 釋放 malloc 得到的指針。


    而new[]則稍微復雜一點,當你調用 new ClassA[nCount] 申請一個對象個數為 nCount 的 ClassA 數組時,編譯器(MSVC)會調用 malloc 申請一塊大小為 sizeof(ClassA) * nCount + 4 的內存,多出來的 4 bytes 被放在 new[] 關鍵字放在地址 ptr 的前面,其中記錄了數組中元素的個數。當調用 delete[] 刪除數組時,會根據數組首地址前 4 bytes 中記錄元素的個數來依次調用數組中對象的析構函數(每次指針偏移 sizeof(ClassA) 大小),再調用 free 釋放指針 (ptr - 4)。


    因此,混用 new/delete 和 new[]/delete[] 通常會導致內存訪問崩潰。然后這里用了“通常”,也就是說在某些特定情況下,混用 new/delete 和 new[]/delete[] 是不會有任何影響的:

  • 創建和釋放 C++ 的內建(build-in)類型時,即 int、char等。
  • 創建和釋放“自身和所有成員變量都不含自定義構造函數和析構函數”的類型。(這一條可能依賴于編譯器的實現,至少在 MSVC 中此情況成立)

  • 然而當項目代碼一旦復雜起來,要分清什么時候上面兩個條件能夠成立就不是那么輕松的事了,因此最好的方法就是無論何時何地都不要混用 new/delete 和 new[]/delete[]。



    這里簡單區分一下,new/delete用來創建和銷毀對象,new[]/delete[]用來創建和銷毀數組。



    三、不要 delete “void” 指針

    類似于下面形式的代碼:

    // Author :大便一籮筐 2016-04-03struct StructA {char cData; }void* pBuffer = new StructA[nSize]; // do something... delete pBuffer;

    可能寫過類似代碼的同學會覺得這種寫法并沒有什么問題,事實也是如此,它能夠正常工作,既不會產生內存泄漏,也不會運行報錯。

    但是,上面情況只能說是一種幸運的巧合,如果發生一些微小的改變,結果就會發生意想不到的變化:

    // Author :大便一籮筐 2016-04-03struct StructA {string strData; }void* pBuffer = new StructA[nSize]; // do something... delete pBuffer;

    細心的同學可能已經看出來了,由于 pBuffer 是 void 指針,delete pBuffer 時,并不會調用 StructA 的析構函數,而這導致了 string 的析構函數也沒有被調用,最終產生的結果就是 string 中的字符串緩沖泄漏。
    有的同學可能會說,C++ 不是支持多態嘛,我把 StructA 的析構函數定義成虛函數不就好了。然而不幸的是,作為 C++ 的內建類型,void 并沒有定義析構函數,因此寄希望于多態是行不通的。
    還有的同學可能會說,如果想定義 void 類型的內存緩沖區怎么辦? ???—— 別忘記我們還有 malloc 和 free。
    所以,任何時候都不要嘗試 delete void 指針。


    android jni new/delete 和 new[]/delete[]就講完了。


    就這么簡單。

    

    總結

    以上是生活随笔為你收集整理的android jni new/delete 和 new[]/delete[]的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。