【C++ Primer | 19】控制內存分配

1. 測試代碼:?

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;void* operator new(size_t size)
{cout << "global Override operator new" << endl;if (void* ptr = malloc(size))  return ptr;elsethrow bad_alloc();
}void* operator new(size_t size, int flag)
{cout << "global Override operator new: " << flag << endl;return (::operator new(size));
}void operator delete (void* ptr)
{cout << "global Override operator delete" << endl;free(ptr);ptr = nullptr;
}void operator delete (void* ptr, int flag)
{cout << "Override operator delete: " << flag << endl;::operator delete(ptr);ptr = nullptr;
}int main()
{int* ptr = new int(10);delete ptr;cout << endl << "------------" << endl << endl;ptr = new(20) int(10);delete ptr;return 0;
}

輸出結果:

分析:

從上面的結果可以看出,new int(10);直接先調用 operator new(size_t size); 由于int沒有構造函數,在那塊內存上調用int的構造函數; 在delete ptr; 的時間直接調用 operator delete(void * ptr);這個函數

new(20) int(10);的時候,則調用重載版本的 operator new(size_t size, int flag); 而該函數有調用了 operator new(size_t size); 函數,釋放的時候delete ptr;還是直接只調用operator delete(void * ptr);(注:這里初步提出為啥不調用operator delete(void * ptr, int flag); 這個函數來釋放ptr ???因為它的用途不在這,而在于下面將要講的。

?

2. 測試代碼:

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;void* operator new(size_t size)
{cout << "global Override operator new" << endl;void* ptr = malloc(size);return ptr;
}void* operator new(size_t size, int flag)
{cout << "global Override operator new: " << flag << endl;return (::operator new(size));
}void operator delete (void* ptr)
{cout << "global Override operator delete" << endl;free(ptr);ptr = nullptr;
}void operator delete (void* ptr, int flag)
{cout << "Override operator delete: " << flag << endl;::operator delete(ptr);ptr = nullptr;
}class Base
{
public:Base(){cout << "Base construct" << endl;throw 2;}~Base(){cout << "Base destructor" << endl;}//類中定制的operator new會覆蓋全局的函數,但可以通過簡單的調用全局的函數來實現調用static void* operator new(size_t size){cout << "operator new of Base" << endl;return ::operator new(size); //調用全局的operator new}static void* operator new(size_t size, int flag){cout << "Override operator new of Base: " << flag << endl;return operator new(size);}static void operator delete(void* ptr){cout << "Operator delete of Base" << endl;::operator delete(ptr);}static void operator delete(void* ptr, int flag){cout << "Override operator delete of Base: " << flag << endl;operator delete(ptr);}int x;int y;
};
int main()
{try{Base* bptr = new(20) Base;}catch (...){cout << "catch a exception" << endl;}return 0;
}

輸出結果:

?

2. 若是不給Base類重載?static void operator delete(void * ptr, int flag);這個函數,結果則如下圖:

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;void* operator new(size_t size)
{cout << "global Override operator new" << endl;void* ptr = malloc(size);return ptr;
}void* operator new(size_t size, int flag)
{cout << "global Override operator new: " << flag << endl;return (::operator new(size));
}void operator delete (void* ptr)
{cout << "global Override operator delete" << endl;free(ptr);ptr = nullptr;
}void operator delete (void* ptr, int flag)
{cout << "Override operator delete: " << flag << endl;::operator delete(ptr);ptr = nullptr;
}class Base
{
public:Base(){cout << "Base construct" << endl;throw 2;}~Base(){cout << "Base destructor" << endl;}//類中定制的operator new會覆蓋全局的函數,但可以通過簡單的調用全局的函數來實現調用static void* operator new(size_t size){cout << "operator new of Base" << endl;return ::operator new(size); //調用全局的operator new}static void* operator new(size_t size, int flag){cout << "Override operator new of Base: " << flag << endl;return operator new(size);}static void operator delete(void* ptr){cout << "Operator delete of Base" << endl;::operator delete(ptr);}int x;int y;
};
int main()
{try{Base* bptr = new(20) Base;}catch (...){cout << "catch a exception" << endl;}return 0;
}

輸出結果:

?

?

二、定位new表達式

operator new和operator delete和alloctor類的allocate和deallocate很像,都是負責分配和釋放內存的函數,但是對于operator new分配的內存空間我們無法使用construct函數構造對象,我們應該使用new的定位new形式構造對象。

1. 測試代碼:

#include <iostream>
#include <new>
using namespace std;const int chunk = 16;
class Foo
{
public:int val() { return _val; }Foo() { _val = 0; }
private:int _val;
};//預分配內存,但沒有Foo對象
char* buf = new char[sizeof(Foo) * chunk];
int main(void)
{//在buf中創建一個Foo對象Foo* pb = new (buf) Foo;//檢查一個對象是否被放在buf中if (pb->val() == 0){cout << "new expressio worked!" << endl;}//到這里不能再使用pbdelete[] buf;return 0;
}

placement new的作用就是:創建對象但是不分配內存,而是在已有的內存塊上面創建對象。用于需要反復 創建并刪除的對象上,可以降低分配釋放內存的性能消耗。定位new表達式(placement new expression),允許程序員將對象創建在已經被分配好的內存中,new表的式的形式如下:

new (place_address) type
new (palce_address) type (initializer-list)

【Note】:?

  • 當只傳入一個指針類型的實參時,定位new表達式構造對象但是不分配內存,這個指針沒有要求,甚至可能是一個不是一個指向動態內存的指針
  • 調用析構函數會銷毀對象,但是不會釋放內存。

?

2. 測試代碼:

#include <iostream>
using namespace std;
char addr1[100];
int main()
{cout << "******定位new表達式演示***by David***" << endl;char addr2[100];char *addr3 = new char[100];cout << "addr1 = " << (void*)addr1 << endl;cout << "addr2 = " << (void*)addr2 << endl;cout << "addr3 = " << (void*)addr3 << endl;int *p = nullptr;p = new(addr1)int;  把內存分配到靜態區*p = 1;cout << (void*)p << "  " << *p << endl;p = new(addr2)int; 把內存分配到棧區*p = 2;cout << (void*)p << "  " << *p << endl;p = new(addr3)int;  //把內存分配到堆區*p = 3;cout << (void*)p << "  " << *p << endl;return 0;
}

輸出結果:

?

參考資料

  • 定制自己的new和delete:operator new 和 operator delete
  • 特殊的工具和技術

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385372.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385372.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385372.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

getuid、geteuid、getgid和getegid函數

#include <unistd.h> #include <sys/types.h> uid_t getuid(void); uid_t geteuid(void); 作用&#xff1a;getuid返回當前進程的實際用戶ID&#xff1b;geteuid返回當前用戶的有效用戶ID。這兩個總是成功&#xff0c;不會失敗。 #include <unistd.h> #…

【第15章】虛函數

一、為什么基類中的析構函數要聲明為虛析構函數&#xff1f; 直接的講&#xff0c;C中基類采用virtual虛析構函數是為了防止內存泄漏。具體地說&#xff0c;如果派生類中申請了內存空間&#xff0c;并在其析構函數中對這些內存空間進行釋放。假設基類中采用的是非虛析構函數&am…

進程共享(讀時共享寫時復制)

父子進程之間在剛fork后。父子相同處: 全局變量、.data、.bbs、.text、棧、堆、環境變量、用戶ID、宿主目錄&#xff08;進程用戶家目錄&#xff09;、進程工作目錄、信號處理方式等等&#xff0c;即0~3G的用戶空間是完全一樣的。父子不同處: 1.進程ID 2.fork返回值 3.父進…

【C++ Primer | 08】IO庫

一、istringstream類 描述&#xff1a;從流中提取數據&#xff0c;支持 >> 操作 這里字符串可以包括多個單詞&#xff0c;單詞之間使用空格分開 #include <iostream> #include <sstream> using namespace std; int main() {istringstream istr(&quo…

gdb調試(如何跟蹤指定進程)

使用gdb調試的時候&#xff0c;gdb只能跟蹤一個進程。可以在fork函數調用之前&#xff0c;通過指令設置gdb調試工具跟蹤父進程或者是跟蹤子進程。默認跟蹤父進程。 set follow-fork-mode child 命令設置gdb在fork之后跟蹤子進程。 set follow-fork-mode parent 設置跟蹤父進程…

【Leetcode | 01】Backtracking

回溯算法序號題號117. 電話號碼的字母組合222. 括號生成 39. 組合總和 40. 組合總和 II 46. 全排列 47. 全排列 II 60. 第k個排列 77. 組合 78. 子集 90. 子集 II 93. 復原IP地址 131. 分割回文串 216. 組合總和 III 306. 累加數 357. 計算各個位數不同的數字個數 401. 二進…

1033. 舊鍵盤打字(20)

舊鍵盤上壞了幾個鍵&#xff0c;于是在敲一段文字的時候&#xff0c;對應的字符就不會出現。現在給出應該輸入的一段文字、以及壞掉的那些鍵&#xff0c;打出的結果文字會是怎樣&#xff1f; 輸入格式&#xff1a; 輸入在2行中分別給出壞掉的那些鍵、以及應該輸入的文字。其中對…

1038. 統計同成績學生(20)

本題要求讀入N名學生的成績&#xff0c;將獲得某一給定分數的學生人數輸出。 輸入格式&#xff1a; 輸入在第1行給出不超過105的正整數N&#xff0c;即學生總人數。隨后1行給出N名學生的百分制整數成績&#xff0c;中間以空格分隔。最后1行給出要查詢的分數個數K&#xff08;不…

EXEC函數族的一般規律

事實上&#xff0c;只有execve是真正的系統調用&#xff0c;其它五個函數最終都調用execve&#xff0c;所以execve在man手冊第2節&#xff0c;其它函數在man手冊第3節。這些函數之間的關系如下圖所示。

exit與_exit函fork與vfork函數

#include <stdlib.h> void exit(int status); #include <unistd.h> void _exit(int status); exit函數與_exit函數一樣&#xff0c;都是系統函數&#xff0c;且都是用來終止一個進程的&#xff0c;無論在程序中的什么位置&#xff0c;只要執行這exit或_exit系統…

1042. 字符統計(20)

請編寫程序&#xff0c;找出一段給定文字中出現最頻繁的那個英文字母。 輸入格式&#xff1a; 輸入在一行中給出一個長度不超過1000的字符串。字符串由ASCII碼表中任意可見字符及空格組成&#xff0c;至少包含1個英文字母&#xff0c;以回車結束&#xff08;回車不算在內&#…

孤兒進程與僵尸進程

孤兒進程&#xff1a;父進程先于子進程結束&#xff08;遇到return、exit、異常終止等情況時&#xff09;&#xff0c;則子進程成為孤兒進程&#xff0c;子進程的父進程成為init進程&#xff0c;稱為init進程領養孤兒進程。可以通過getppid函數來查看孤兒進程的父進程ID&#x…

1043. 輸出PATest(20)

給定一個長度不超過10000的、僅由英文字母構成的字符串。請將字符重新調整順序&#xff0c;按“PATestPATest....”這樣的順序輸出&#xff0c;并忽略其它字符。當然&#xff0c;六種字符的個數不一定是一樣多的&#xff0c;若某種字符已經輸出完&#xff0c;則余下的字符仍按P…

wait函數

#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); 僵尸進程。進程結束后放棄了幾乎所有的內存空間&#xff0c;沒有任何可以執行的代碼&#xff0c;也不能被調度&#xff0c;僅僅在進程列表中保留著一個位置&#xff0c;記載著該進程的退出…

map與unordered_map

時間復雜度&#xff1a; mapunordered_mapOrderingincreasing orderno orderImplementationSelf balancing BSTHash Tablesearch timelog(n) O(1&#xff09;: 平均水ping O(n&#xff09;&#xff1a;最糟糕情況 Insertion timelog(n) RebalanceSame sa searchDelete timelo…

waitpid函數

#include <sys/wait.h> #include <sys/types.h> pid_t waitpid(pid_t pid, int *status, int options); 作用&#xff1a;同wait&#xff0c;但可指定pid進程清理&#xff0c;可以不阻塞。 waitpid函數的第二個參數int *status跟wait函數的形參一樣&#xff0c;…

進程間通信的方法

Linux環境下&#xff0c;進程地址空間相互獨立&#xff0c;每個進程各自有不同的用戶地址空間。任何一個進程的全局變量在另一個進程中都看不到&#xff0c;所以進程和進程之間不能相互訪問&#xff0c;要交換數據必須通過內核&#xff0c;在內核中開辟一塊緩沖區&#xff0c;進…

管道的概念(匿名管道)

管道是一種最基本的IPC機制&#xff0c;作用于有血緣關系的進程之間&#xff0c;完成數據傳遞。調用pipe系統函數即可創建一個管道。管道有如下特質&#xff1a;1.其本質是一個偽文件&#xff08;實為內核緩沖區&#xff09;&#xff0c;偽文件即不是真正的文件&#xff0c;不占…

1023. 組個最小數 (20)

給定數字0-9各若干個。你可以以任意順序排列這些數字&#xff0c;但必須全部使用。目標是使得最后得到的數盡可能小&#xff08;注意0不能做首位&#xff09;。例如&#xff1a;給定兩個0&#xff0c;兩個1&#xff0c;三個5&#xff0c;一個8&#xff0c;我們得到的最小的數就…

pipe函數

#include <unistd.h> int pipe(int pipefd[2]); 作用&#xff1a;創建管道 成功&#xff1a;0&#xff1b;失敗&#xff1a;-1&#xff0c;設置errno 函數調用成功返回r/w兩個文件描述符。無需open&#xff0c;但需手動close。規定&#xff1a;fd[0] …