More Effective C++ (運算符)

4.1:謹慎定義類型轉換函數

<1>容易的方法是利用一個最新的編譯器特性:explicit關鍵字

<2>C++編譯器把">>"作為一個符號來解釋,在兩個">"間沒有空格,語句會產生語法錯誤。

<3>隱式類型轉換函數

示例代碼如下:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 template<class T>
 5 class Array
 6 {
 7 public:
 8     class ArraySize
 9     {
10         public:
11             ArraySize(int numElements):theSize(numElements)
12             {
13                 cout<<"flag ArraySize(int  numElements)"<<endl;
14             }
15             int size() const
16             {
17                 cout<<"flag  size()"<<endl; 
18                 return theSize;
19             }
20         private:
21             int theSize;
22     };
23 
24     Array(int lowBound,int highBound)
25         {};
26 
27     Array(ArraySize arsize)
28     {
29         cout<<"flag Array"<<endl;
30     };
31 };
32 void main()
33 {
34     Array<int>  a(10);
35 }
36 
37 /*
38 flag ArraySize(int  numElements)
39 flag Array
40  */

4.2:自增,自減操作符前綴形式和后綴形式

<1>重載函數間的區別決定于它們的參數類型上的差異,但是不論increament或者decrement的前綴還是后綴都只有一個參數。

為了解決這個語言問題,C++規定后綴形式有一個int類型參數,當函數被調用時,編譯器傳遞一個0作為int參數的值給該函數。

<2>前綴自增:增加然后取回;后綴自增:取回然后增加

<3>前綴形式

示例代碼如下:

 1 UPInt & UPInt::operator++()
 2 {
 3     *this += 1;   //增加
 4     return *this; //取回值
 5 }
 6 const UPInt UPInt::operator++(int)
 7 {
 8     UPInt oldValue = *this;   //保留原值
 9     ++(*this);          //增加
10     return oldValue;    //返回原值
11 }

<4>后綴操作符函數沒有使用它的參數。這個參數僅僅只是為了區別前綴函數調用。

<5>如果在函數內部沒有使用參數,許多編譯器會顯示警告信息。為了避免這些信息,最常用的方式就是省略掉參數名稱

<6>很明顯后綴函數必須返回一個對象,但是為什么是const對象呢?假設不是const:

假設不是 const 對象,下面的代碼就是正確的:

這組代碼與下面的代碼相同:?

i.operator++(0).operator++(0); ?

很明顯, 第一個調用的operator++函數返回的對象調用了第二個operator++函數。 有兩個理由導致我們應該厭惡上述這種做法:

第一,是與內置類型行為不一致。當設計一個類遇到問題時,一個好的準則是使該類的行為與int 類型一致。而int 類型不允許連續進行兩次后綴 increment:?

int i;?

i++++; // 錯誤!?

第二個原因,是使用兩次后綴 increment 所產生的結果與調用者期望的不一致。

如上所示,第二次調用operator++改變的值是第一次調用返回對象的值,而不是原始對象的值。因此如果:

i++++; 是合法的,i 將僅僅增加了一次。這與人的直覺相違背,使人迷惑(對于int類型和 UPInt 都是一樣),所以最好禁止這么做。

C++禁止int 類型這么做,同時你也必須禁止你自己寫的類有這樣的行為。

最容易的方法是讓后綴 increment 返回 const 對象。當編譯器遇到這樣的代碼: ?

i++++; // same as ?

i.operator++(0).operator++(0);

它發現從第一個 operator++函數返回的 const 對象又調用 operator++函數,然而這個函數是一個 non-const 成員函數, 所以 const 對象不能調用這個函數。

如果你原來想過讓一個函數返回 const 對象沒有任何意義,現在你就知道有時還是有用的,后綴 increment和 decrement 就是例子。 (更多的例子參見 Effective C++ 條款 21)?


如果你很關心效率問題, 當你第一次看到后綴 increment函數時,你可能覺得有些問題。

這個函數必須建立一個臨時對象以做為它的返回值, (參見條款M19) ,上述實現代碼建立了一個顯式的臨時對象(oldValue) ,這個臨時對象必須被構造并在最后被析構。前綴 increment 函數沒有這樣的臨時對象。

由此得出一個令人驚訝的結論,如果僅為了提高代碼效率,UPInt 的調用者應該盡量使用前綴 increment,少用后綴 increment,除非確實需要使用后綴 increment。

讓我們明確一下,當處理用戶定義的類型時,盡可能地使用前綴 increment,因為它的效率較高。


我們再觀察一下后綴與前綴 increment 操作符。它們除了返回值不同外,所完成的功能是一樣的,即值加一。

簡而言之,它們被認為功能一樣。那么你如何確保后綴increment和前綴increment的行為一致呢?

當不同的程序員去維護和升級代碼時,有什么能保證它們不會產生差異?除非你遵守上述代碼里的原則,這才能得到確保。

這個原則是后綴 increment和 decrement 應該根據它們的前綴形式來實現。你僅僅需要維護前綴版本,因為后綴形式自動與前綴形式的行為一致。


正如你所看到的,掌握前綴和后綴 increment和decrement 是容易的。

一旦了解了他們正確的返回值類型以及后綴操作符應該以前綴操作符為基礎來實現的規則,就足夠了。

4.3:不要重載“&&”,“||”,或“,”

<1>char *p; if((p!=0)&&(strlen(p)>10))......

注意這個if條件的寫法順序

<2>運算符重載要求:

4.4:理解各種不同含義的new和delete

<1>new operator 稱為new操作符與operator new稱為new操作

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

使用的是new operator ,即就是new操作符,這種用法是不可改變的,功能總是相同的。

完成兩部分任務:第一,分配足夠的內存以便容納所需類型的對象;第二,它調用構造函數初始化內存中的對象。

而我們所能改變的是如何為對象分配內存。

new操作符調用一個函數來完成必需的內存分配,你能夠重寫或重載這個函數來改變它的行為。

new操作符為分配內存所調用函數的名字是 operator new。

原型如下:

void * operator new(size_t size);

調用形式與其他函數一樣:

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

placement new旨在完成一種情況,那就是假如我們已經申請到了內存,想要在該內存上構建對象,就選擇placement new

三種情況代碼如下:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 class  Test
 5 {
 6     int a;
 7 public:
 8     Test(int data = 10):a(data)
 9     {
10         cout<<"Construction :"<<this<<endl;
11     }
12     ~Test()
13     {
14         cout<<"Destroy :"<<this<<endl;
15     };
16 };
17 void  main()
18 {
19     //new操作符(new operator)
20     Test *pa = new Test[2];  //完成兩部分任務
21     delete []pa;
22     pa = NULL;
23     
24     //new函數(operator  new)  
25     void  *pb = operator  new(sizeof(Test));  //僅僅只是分配一塊內存,類似于malloc
26     operator  delete(pb);
27     pb = NULL;
28     
29     //placement new 
30     void  *suffer = malloc(sizeof(Test));  
31     Test *pc = new(suffer)  Test(100);  //在已經申請的內存上建立自己的對象
32     pc->~Test();
33     free(suffer);
34     suffer = NULL;
35 }

總結:

如果想在堆上創建一個對象,應該用new操作符,它分配內存,同時又為對象調用構造函數。

如果僅僅想分配內存,就用operator new函數,它不會調用構造函數

如果你想定制自己的在堆對象被建立時的內存分配過程,應該重載寫你自己的operator new函數,new操作符會調用你定制的operator new

如果想在一塊已經分配好的內存里建立一個對象,使用placement new

<2>delete操作符與operator delete的關系與new操作符與operator new的關系一樣

delete p;

導致編譯器生成類似的代碼:

p->~Class();

operator delete(p);

?

轉載于:https://www.cnblogs.com/Braveliu/archive/2013/01/06/2847167.html

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

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

相關文章

大綱(二)

一、數據結構就是邏輯結構存儲結構(物理結構)相應操作(算法實現) 二、邏輯結構 集合1:1 線性結構1:n 樹m:n 圖 主要是可以畫到紙上進行分析的結構圖就是邏輯結構&#xff0c;分析問題可以得出唯一一個邏輯結構 三、存儲結構(物理結構) 順序存儲結構(例如:線性表)鏈式存儲結…

php微信獲取mediaid超出限制_Python實現每日微信自動打卡

眾所周知&#xff0c;因為疫情的原因&#xff0c;很多高校和公司都要求員工每日在微信上進行打卡&#xff0c;來匯報自己的當前身體狀態和所處地區。但絕大多數情況下&#xff0c;每天打卡的信息其實是不會變的&#xff0c;我們要做的就是進入公眾號——自動登錄點進打卡頁面—…

[原創]INI文件的讀寫操作

INI文件的讀寫操作在程序開發中&#xff0c;很多人會把參數保存為ini文件格式&#xff0c;ini文件的一個好處是可以保存為一個結構體主結構&#xff0c;如 [User] Nametest UserIdtest [Server] ServerIp127.0.0.1 ServerPort80 …… 很方便也很容易區分&#xff0c;而且不同節…

Java ResourceBundle keySet()方法及示例

ResourceBundle類keySet()方法 (ResourceBundle Class keySet() method) keySet() method is available in java.util package. keySet()方法在java.util包中可用。 keySet() method is used to get all the existing keys from this ResourceBundle and its super bundles to …

ffplay 分析(音頻從Frame(解碼后)隊列取數據到SDL輸出)

《ffplay的數據結構分析》 《ffplay分析&#xff08;從啟動到讀取線程的操作&#xff09;》 《ffplay分析&#xff08;視頻解碼線程的操作&#xff09;》 《ffplay分析&#xff08;音頻解碼線程的操作&#xff09;》 《ffplay分析 &#xff08;視頻從Frame(解碼后)隊列取數據到…

緒論(一)

一、問題驅動—>畫出唯一的邏輯結構—>定義存儲結構—>實現相應的操作 二、算法—>(定義\特點)步驟—>實現—>評價標準 算法有五大特點&#xff1a; 可行性確定性有窮性(有限性)0個或0個以上的輸入至少一個以上的輸出 評價標準&#xff1a; 時間復雜度 …

利用 dbghelp.dll 生成 dump 文件

dbghelp.dll windows的系統目錄system32下&#xff0c;都有dbghelp.dll&#xff0c;但在實際使用時&#xff0c;往往會讓exe加載自己目錄下的dll&#xff0c;以避免系統目錄下的dll版本不一導致的程序異常。 故一般都是用LoadLibrary()的方式加載Dll&#xff0c;先加載當前目錄…

pandas 根據列名索引多列數據_Pandas 數據聚合與分組運算[groupby+apply]速查筆記

利用Pandas將數據進行分組&#xff0c;并將各組進行聚合或自定義函數處理。Pandas中Groupby分組與聚合過程導入模塊import pandas as pd縮寫df表示Dataframe對象分組df.groupby(col1)&#xff1a; 根據col1列將df全部列分組&#xff08;默認&#xff1a;axis0行&#xff09;df[…

殺毒軟件對InstallShield編譯過程以及安裝包運行的影響

版權聲明: 可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息。在某些情況下&#xff0c;殺毒軟件會導致InstallShield編譯過程崩潰。比如 McAfee VirusScan Enterprise 8.5.0i版本&#xff0c;在某些情況下會在InstallScript工程編譯過程中&#xff0c;將ISSe…

Java FileInputStream close()方法與示例

FileInputStream類close()方法 (FileInputStream Class close() method) close() method is available in java.io package. close()方法在java.io包中可用。 close() method is used to close this FileInputStream and free all system resources linked with this stream. c…

ffplay分析 (視頻從Frame(解碼后)隊列取數據到SDL輸出)

《ffplay的數據結構分析》 《ffplay分析&#xff08;從啟動到讀取線程的操作&#xff09;》 《ffplay分析&#xff08;視頻解碼線程的操作&#xff09;》 《ffplay分析&#xff08;音頻解碼線程的操作&#xff09;》 《ffplay 分析&#xff08;音頻從Frame(解碼后)隊列取數據到…

線性結構節點類型(三)

一、線性結構 特點 第一個數據元素沒有前驅最后一個數據元素沒有后繼1:1邏輯上相鄰、物理上也相鄰 類型 線性表(就是一張二維表)(為主研究對象)棧隊列 學習方法 畫邏輯結構—>定義存儲結構—>實現相應的操作 二、線性表 線性結構 邏輯上的1:1存儲結構 順序存儲結…

PL/SQL詳細介紹

PL/SQL筆記PL/SQL塊中只能直接嵌入SELECT,DML(INSERT,UPDATE,DELETE)以及事務控制語句(COMMIT,ROLLBACK,SAVEPOINT),而不能直接嵌入DDL語句(CREATE,ALTER,DROP)和DCL語句(GRANT,REVOKE) 1.檢索單行數據 1.1使用標量變量接受數據 v_ename emp.ename%type; v_sal emp.sal%…

redis 備份導出rdb_Redis數據遷移利器之redisshake

“當需要進行Redis實例或集群數據遷移時&#xff0c;我們可以采用導出/導入的方式進行數據遷移&#xff0c;但當需要做數據異地災備或雙活時&#xff0c;再使用傳統的方式就不合適了&#xff0c;我們需要借助工具(如redis-port/redis-shake)來完成。”redis-shake介紹redis-sha…

從Live Space搬家到這里

聽說Live Space很快要關閉了&#xff0c;所以從http://peking2toronto.spaces.live.com/搬家到這里。轉載于:https://www.cnblogs.com/pentest/archive/2010/08/29/1811726.html

java 方法 示例_Java Collectionsfrequency()方法與示例

java 方法 示例集合類的frequency()方法 (Collections Class frequency() method) frequency() method is available in java.util package. frequency()方法在java.util包中可用。 frequency() method is used to return the frequency of the given Object (obj) to the give…

線性結構常規操作(四)

定義存儲結構(以單向鏈表為主) 對于鏈表的定義&#xff0c;通過結構體進行定義&#xff0c;包括兩部分&#xff0c;一是數據域&#xff0c;另一個就是指針&#xff0c;用于指向下一個節點。 1&#xff0c;創建鏈表 定義鏈表&#xff1a; struct nodesq{int data;//數據域&a…

ffplay分析 (暫停 / 播放處理)

《ffplay的數據結構分析》 《ffplay分析&#xff08;從啟動到讀取線程的操作&#xff09;》 《ffplay分析&#xff08;視頻解碼線程的操作&#xff09;》 《ffplay分析&#xff08;音頻解碼線程的操作&#xff09;》 《ffplay 分析&#xff08;音頻從Frame(解碼后)隊列取數據到…

源碼 狀態機_[源碼閱讀] 阿里SOFA服務注冊中心MetaServer(1)

[源碼閱讀] 阿里SOFA服務注冊中心MetaServer(1)0x00 摘要0x01 服務注冊中心1.1 服務注冊中心簡介1.2 SOFARegistry 總體架構1.3 為什么要分層0x02 MetaServer2.1簡介2.2 問題0x03 代碼結構0x04 啟動運行4.1 集成部署4.2 獨立部署0x05 總體邏輯5.1 程序主體5.2 配置0x06 啟動6.1…

HttpService遠程校驗

今天學了下HttpService&#xff0c;和大家分享一下。HttpService是用來讀取遠程數據的一個對象&#xff0c;數據格式為XML。 我做了一個登陸校驗的功能&#xff0c;主要是通過HttpService將服務器端的用戶數據得到&#xff0c;然后在客戶端判斷輸入的用戶名和密碼是否存在。 主…