C++ 中 new 操作符內幕:new operator、operator new、placement new

一、new 操作符(new operator)

人們有時好像喜歡有意使C++語言的術語難以理解。比方說new操作符(new operator)和operator new的差別。

當你寫這種代碼:

string *ps = new string("Memory Management");

你使用的new是new操作符
這個操作符就象sizeof一樣是語言內置的。你不能改變它的含義,它的功能總是一樣的。它要完畢的功能分成兩部分。第一部分是分配足夠的內存以便容納所需類型的對象。

第二部分是它調用構造函數初始化內存中的對象。new操作符總是做這兩件事情,你不能以不論什么方式改變它的行為。
(總結就是,new操作符做兩件事,分配內存+調用構造函數初始化。你不能改變它的行為。)

二、operator new

你所能改變的是怎樣為對象分配內存

new操作符調用一個函數來完畢必需的內存分配,你可以重寫或重載這個函數來改變它的行為。new操作符為分配內存所調用函數的名字是operator new。
函數operator new 通常這樣聲明:

void * operator new(size_t size);

返回值類型是void*,由于這個函數返回一個未經處理(raw)的指針。未初始化的內存。(假設你喜歡。你能寫一種operator new函數,在返回一個指針之前可以初始化內存以存儲一些數值,可是一般不這么做。)參數size_t確定分配多少內存。

你能添加額外的參數重載函數operator new,可是第一個參數類型必須是size_t。如: new(__FILE__, __LINE__)

(有關operator new很多其它的信息參見Effective C++ 條款8至條款10。)

你一般不會直接調用operator new,可是一旦這么做。你能夠象調用其他函數一樣調用它:

void *rawMemory = operator new(sizeof(string));

操作符operator new將返回一個指針,指向一塊足夠容納一個string類型對象的內存。
就象malloc一樣,operator new的職責僅僅是分配內存。

它對構造函數一無所知。operator new所了解的是內存分配。把operator new 返回的未經處理的指針傳遞給一個對象是new操作符的工作。當你的編譯器遇見這種語句:

string *ps = new string("Memory Management");

它生成的代碼或多或少與以下的代碼相似(很多其它的細節見Effective C++條款8和條款10。還有我的文章Counting object里的凝視。):

void *memory = operator new(sizeof(string)); // 得到未經處理的內存,為String對象
call string::string("Memory Management") on *memory; // 內存中的對象
string *ps = static_cast<string*>(memory); // 使ps指針指向新的對象

注意第二步包括了構造函數的調用,你做為一個程序猿被禁止這樣去做。你的編譯器則沒有這個約束,它能夠做它想做的一切。
因此假設你想建立一個堆對象就必須用new操作符。不能直接調用構造函數來初始化對象。(總結:operator new是用來分配內存的函數,為new操作符調用。能夠被重載(有限制)

三、placement new

有時你確實想直接調用構造函數。在一個已存在的對象上調用構造函數是沒有意義的,由于構造函數用來初始化對象。而一個對象只能在給它初值時被初始化一次。

可是有時你有一些已經被分配可是尚未處理的的(raw)內存,你須要在這些內存中構造一個對象。你能夠使用一個特殊的operator new ,它被稱為placement new。

以下的樣例是placement new怎樣使用,考慮一下:

class Widget {public:Widget(int widgetSize);...
};Widget * constructWidgetInBuffer(void *buffer,int widgetSize)
{return new (buffer) Widget(widgetSize);
}

這個函數返回一個指針。指向一個Widget對象,對象在轉遞給函數的buffer里分配。
當程序使用共享內存或memory-mapped I/O時這個函數可能實用,由于在這樣程序里對象必須被放置在一個確定地址上或一塊被例程分配的內存里。(參見條款4,一個怎樣使用placement new的一個不同樣例。)

在constructWidgetInBuffer里面。返回的表達式是: new (buffer) Widget(widgetSize)
這初看上去有些陌生,可是它是new操作符的一個使用方法,須要使用一個額外的變量(buffer)。當new操作符隱含調用operator new函數時。把這個變量傳遞給它。被調用的operator new函數除了帶有強制的參數size_t外,還必須接受void*指針參數。指向構造對象占用的內存空間。這個operator new就是placement new,它看上去象這樣:

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

這可能比你期望的要簡單,可是這就是placement new須要做的事情。畢竟operator new的目的是為對象分配內存然后返回指向該內存的指針。在使用placement new的情況下,調用者已經獲得了指向內存的指針。由于調用者知道對象應該放在哪里。placement new必須做的就是返回轉遞給它的指針。(沒實用的(可是強制的)參數size_t沒有名字,以防止編譯器發出警告說它沒有被使用。見條款6。
) placement new是標準C++庫的一部分。為了使用placement new。你必須使用語句#include <new>(或者假設你的編譯器還不支持這新風格的頭文件名稱)。

(總結:placement new是一種特殊的operator new,作用于一塊已分配但未處理或未初始化的raw內存)

四、小結

讓我們從placement new回來片刻,看看new操作符(new operator)與operator new的關系,(new操作符調用operator new)

  • 你想在堆上建立一個對象,應該用new操作符。它既分配內存又為對象調用構造函數。
  • 假設你只想分配內存,就應該調用operator new函數;它不會調用構造函數。
  • 假設你想定制自己的在堆對象被建立時的內存分配過程,你應該寫你自己的operator new函數。然后使用new操作符,new操作符會調用你定制的operator new。
  • 假設你想在一塊已經獲得指針的內存里建立一個對象。應該用placement new。

五、Deletion and Memory Deallocation

為了避免內存泄漏,每一個動態內存分配必須與一個等同相反的deallocation相應。

1.函數operator delete與delete操作符的關系與operator new與new操作符的關系一樣。當你看到這些代碼:

string *ps;
...
delete ps; // 使用delete 操作符

你的編譯器會生成代碼來析構對象并釋放對象占有的內存。
Operator delete用來釋放內存。它被這樣聲明:

void operator delete(void *memoryToBeDeallocated);

因此, delete ps; 導致編譯器生成類似于這種代碼:

ps->~string(); // call the object's dtor
operator delete(ps); // deallocate the memory the object occupied

這有一個隱含的意思是假設你僅僅想處理未被初始化的內存,你應該繞過new和delete操作符,而調用operator new 獲得內存和operator delete釋放內存給系統:

void *buffer = operator new(50*sizeof(char)); // 分配足夠的內存以容納50個char//沒有調用構造函數...
operator delete(buffer); // 釋放內存// 沒有調用析構函數

這與在C中調用malloc和free等同。
2.placement new建立的對象怎樣釋放?
假設你用placement new在內存中建立對象,你應該避免在該內存中用delete操作符。

由于delete操作符調用operator delete來釋放內存,可是包括對象的內存最初不是被operator new分配的。placement new僅僅是返回轉遞給它的指針。誰知道這個指針來自何方?而你應該顯式調用對象的析構函數來解除構造函數的影響:

// 在共享內存中分配和釋放內存的函數 void * mallocShared(size_t size);void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = // 如上所看到的,
constructWidgetInBuffer(sharedMemory, 10); // 使用// placement new ...
delete pw; // 結果不確定! 共享內存來自
// mallocShared, 而不是operator newpw->~Widget(); // 正確。 析構 pw指向的Widget,// 可是沒有釋放
//包括Widget的內存freeShared(pw); // 正確。 釋放pw指向的共享內存// 可是沒有調用析構函數

如上例所看到的,假設傳遞給placement new的raw內存是自己動態分配的(通過一些不經常使用的方法),假設你希望避免內存泄漏,你必須釋放它。(參見我的文章Counting objects里面關于placement delete的凝視。)

六、數組

到眼下為止一切順利。可是還得接著走。

到眼下為止我們所測試的都是一次建立一個對象。

如何分配數組?會發生什么?

string *ps = new string[10]; // allocate an array of objects

被使用的new仍然是new操作符,可是建立數組時new操作符的行為與單個對象建立有少許不同。
第一是內存不再用operator new分配,取代以等同的數組分配函數,叫做operator new[](常常被稱為array new)。

它與operator new一樣能被重載。

這就同意你控制數組的內存分配。就象你能控制單個對象內存分配一樣(可是有一些限制性說明,參見Effective C++ 條款8)。

(operator new[]對于C++來說是一個比較新的東西。所以你的編譯器可能不支持它。假設它不支持。不管在數組中的對象類型是什么。全局operator new將被用來給每一個數組分配內存。
在這種編譯器下定制數組內存分配是困難的。由于它須要重寫全局operator new。這可不是一個能輕易接受的任務。

缺省情況下,全局operator new處理程序中全部的動態內存分配,所以它行為的不論什么改變都將有深入和普遍的影響。并且全局operator new有一個正常的簽名(normal signature)(也就單一的參數size_t。參見Effective C++條款9)。所以假設你 決定用自己的方法聲明它,你立馬使你的程序與其他庫不兼容基于這些考慮,在缺乏operator new[]支持的編譯器里為數組定制內存管理不是一個合理的設計。)

第二個不同是new操作符調用構造函數的數量。對于數組,在數組里的每個對象的構造函數都必須被調用:

string *ps = new string[10]; // 調用operator new[]為10個string對象分配內存,// 然后對每一個數組元素調用string對象的缺省構造函數。

相同當delete操作符用于數組時,它為每一個數組元素調用析構函數,然后調用operator delete來釋放內存。(buxizhizhou530注:這里應該是operator delete[]吧)
就象你能替換或重載operator delete一樣,你也替換或重載operator delete[]。

在它們重載的方法上有一些限制。

請參考優秀的C++教材。

(總結:數組時,兩個不同點,一時調用operator new[]函數,二是new操作符調用構造函數的數量不同。)

七、總結

new和delete操作符是內置的,其行為不受你的控制。凡是它們調用的內存分配和釋放函數則能夠控制。當你想定制new和delete操作符的行為時,請記住你不能真的做到這一點。你僅僅能改變它們為完畢它們的功能所採取的方法,而它們所完畢的功能則被語言固定下來。不能改變。(You can modify how they do what they do, but what they do is fixed by the language)
參考:C++中delete, new以及new [], delete[]操作符內幕
本文大部分內容來源于上述博文,可惜這個博文也是轉載,沒有找到原出處。本文僅是依據上述博文內容,加上自己理解,進行了又一次排版、顏色標注、相關改動、及自己的總結。

假設錯誤。歡迎交流~

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

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

相關文章

Flask-1-05-CookieSession

接下來我會演示一下設置Cookie 讀取Cookie 刪除Cookie&#xff0c;以及添加Cookie的原理 接下來我們分別定義3個視圖為 set_cookie、get_cookie、del_cookie # coding:utf-8from flask import Flask, make_response, requestapp Flask(__name__)app.route("/set_cookie&q…

18個不可不知的有用潛規則

人情世故是我們日常生活中積累的約定俗成的行為規則&#xff0c;屬于社會知識的范疇。這些知識大半來源于與不同人群的社會交際&#xff0c;也來源于社會沖突與社會發展。在有專業知識與技能的情況下&#xff0c;人情世故能夠幫助我們個人緩和與其他人之間的緊張度&#xff0c;…

解決跨域問題:No ‘Access-Control-Allow-Origin‘ header is present on the requested resource.

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 PS&#xff1a;如果遇到 這個問題 Request header field Content-Type is not allowed by Access-Control-Allow-Headers&#xff0c;解…

錯誤代碼:0xc000007b 解決思路

三維電子沙盤項目&#xff1a; 現場環境部署時&#xff0c;二維平臺和模型在實際部署中遇到了0xc000007b的問題&#xff0c;網上很多說是DX的問題&#xff0c;但并不能解決。 之前在編譯OpenDDS時也遇到過類似的問題&#xff0c;是在不同版本的OpenDDS的動態庫混用時遇到的&a…

上傳本地項目到git

1、到需要上傳的文件夾下&#xff0c;打開gitbase 2、git init 3、git add . 4、git commit -m init 5、驗證權限 首先在Git Bash中輸入:ssh-keygen -t rsa -C "youremailxxx.com" 然后一路回車,這個會在當前用戶文件夾下&#xff0c;生成.ssh 文件夾&#xff0c;里邊…

vue 設置全局變量、指定請求的 baseurl

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 安裝 axios&#xff1a; npm install axios --save-dev 2. 新建一個 Base.vue 文件&#xff0c;書寫內容如下&#xff1a; <sc…

解決這九種困擾 可以讓你每天精神百倍

晚上睡眠是人體最好的休息方式&#xff0c;人體的很多癥狀都會在充足的睡眠后得到緩解&#xff0c;良好的睡眠比世界上的任何藥都靈&#xff0c;而現代社會&#xff0c;“一覺睡到自然醒”已是很多人可望而不可及的了。我經過多年的仔細觀察&#xff0c;發現只要是在早晨醒來后…

基于java的數據結構學習——泛型動態數組的封裝

public class Array<E> {private E[] data;private int size;// 構造函數public Array(int Capacity){data (E[])new Object[Capacity];size 0;}// 默認構造函數public Array(){this(10);}// 判斷數組是否已滿public boolean isFull(){return size data.length;}// 判…

POJ1207-The 3n + 1 problem

http://poj.org/problem?id1207 注意輸入兩個數的大小&#xff0c;水。。。 #include <stdio.h> int main(void) {int a,b,m,count,i,max;while(scanf("%d%d",&a,&b)!EOF){printf("%d %d ",a,b);if(a>b){ma;ab;bm;}max0;for(ia;i<b;i…

解決:vue.esm.js?efeb:591 [Vue warn]: Do not use built-in or reserved HTML elements as component id: me

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 報錯如題&#xff1a; vue.esm.js?efeb:591 [Vue warn]: Do not use built-in or reserved HTML elements as component id: menu …

JetBrains 系列軟件漢化包

Android Studio 3.0-3.1 漢化包 CLion 2018.1 漢化包 GoLand 2017.3.2-2018.1 漢化包 IntelliJ IDEA 2017.3-2018.1 漢化包 PhpStorm 2017.3-2018.1 漢化包 PyCharm 2017.3-2018.1 漢化包 RubyMine 2017.3.2-2018.1 漢化包 WebStorm 2017.3-2018.1 漢化包 下載完畢后&#xff…

JAXP進行DOM和SAX解析

1.常用XML的解析方式&#xff1a;DOM和SAX 1&#xff09;DOM思想&#xff1a;將整個XML加載內存中&#xff0c;形成文檔對象&#xff0c;所以對XML操作都對內存中文檔對象進行。 2&#xff09;SAX思想&#xff1a;一邊解析&#xff0c;一邊處理&#xff0c;一邊釋放內存資源---…

VScode 格式化代碼快捷鍵、修改快捷鍵

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 請看仔細快捷鍵是&#xff1a; shift alt F // 我是從 eclipse 轉的 idea &#xff0c;現在再用 vscode , 一直條件反射的按的 c…

信息學競賽的常數優化、常見問題、代碼風格相關

在查std::ios::sync_with_stdio(false);有關信息時&#xff0c;看到https://blog.csdn.net/qq_33583069/article/details/53086992 這篇博客&#xff0c;對其中一些問題比較感興趣&#xff0c;整理了下相關資料&#xff1a; isdigit()https://blog.csdn.net/cupidove/article/d…

TCP報文格式詳解

TCP協議只定義了一種報文格式 建立、拆除連接、傳輸數據使用同樣的報文 TCP報文格式 TCP報文段首部&#xff08;20個字節&#xff09; 源端口和目的端口&#xff1a;各占2個字節&#xff0c;16比特的端口號加上32比特的IP地址&#xff0c;共同構成相當于傳輸層服務訪問點的地址…

Vue 生命周期中 mounted( ) 和 created( ) 的區別

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、什么是生命周期&#xff1f; 用通俗的語言來說&#xff0c;就是Vue中實例或者組件從創建到消滅中間經過的一系列過程。雖然不太嚴謹…

科目三考試

馬上要考大路了&#xff0c;考科目三考試必須做到什么事啊&#xff1f; 2012-5-30 8:46:23檢舉  注意事項   1、上車后確認車上電腦信息后&#xff0c;關好車門&#xff0c;系好安全帶&#xff0c;打左轉向燈起步。   2、直線行駛路段必須使用二檔行駛。否則將被判…

基于java的數據結構學習——數組實現的棧以及簡單應用

棧 Stack 棧是一種線性結構相比數組&#xff0c;棧對應的操作是數組的子集只能從一端添加元素&#xff0c;也只能從一端取出元素這一端稱為棧頂棧是一種后進先出的數據結構 棧的應用 無處不在的Undo操作&#xff08;撤銷&#xff09;括號匹配&#xff08;編譯器&#xff09;程…

Python 總結題目

題例1 # 打印如下長方形&#xff1a; ************ * * * * ************ # 打印如下長方形&#xff1a; print("*****************") print("* *") print("* *") print("****************…

vue : 引入、安裝 jquery 、bootstrap

一、vue安裝jquery 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1、新建一個vue工程。 2、在項目文件夾下&#xff0c;使用命令 npm install jquery --save-dev 引入jquery。 np…