Effective C++ 1-3

1.視C++爲一個語言聯邦

c++並不是一個語言,它是由4個次語言組成,分別是C、Object-Oriented C++、Template C++和STL。(其實這4部分不僅可以體現c++的主要特性,也可以從4個方面學習c++,並可以從這4個方面驗證自己對c++瞭解和掌握程度)。因此它的高效編程守則對於同一個對象也會有多種要求。所以c++高效編程守則要視狀況而變化,因爲這取決於你當前狀況偏向於哪個次語言。
舉個栗子:傳參時候並不都是選擇pass-by-reference就是高效,要看其偏向於哪個次語言:如果是內置類型,比如int這種自帶的,因爲其偏向於C語言,所以pass-by-value會更有效;如果是對象,其偏向於Object-Oriented C++,所以pass-by-reference更高效,所以要具體情況具體分析~

總結:選擇哪種高效的原則要看當時狀況偏向哪個次語言。

2. 儘量以const、enum、inline替換#define

1)由於#define定義的常量名未進入symbol table,eg:#define ARRAY_NUM 10 調試時只能看到數字10而不能看到名字ARRAY_NUM,所以導致調試時看到該數字會有點莫名其妙。

2)不能對#define或enum定義的變量名取地址,這個是跟他們存放的內存相關吧~

3)宏定義後,若在某處被#undef,其後該宏就會無效

4)編譯器要在編譯期間知道數組的大小,所以不能醬紫:

int nArrayLength = 10;
int array[nArrayLength];

但是可以醬紫:

const int nArrayLength = 10;
// 或者
enum{nArrayLength = 10};
int array[nArrayLength];

是因爲const修飾後的變量存放位置就不同了?

c++中,一個const不是必需創建內存空間,而在c中,一個const總是需要一塊內存空間。

在c++中是否要爲const全局變量分配內存空間,取決於這個const變量的用途,如果是充當着一個值替換(即就是將一個變量名替換爲一個值),那麼就不分配內存空間,不過當對這個const全局變量取地址或者使用extern時,會分配內存,存儲在只讀數據段。也是不能修改的。

c++中對於局部的const變量要區別對待:

對於基礎數據類型,也就是const int a = 10這種,編譯器會把它放到符號表中,不分配內存,當對其取地址時,會分配內存
對於基礎數據類型,如果用一個變量初始化const變量,如果const int a = b,那麼也是會給a分配內存
對於自定數據類型,比如類對象,那麼也會分配內存。

c中const默認爲外部連接,c++中const默認爲內部連接.當c語言兩個文件中都有const int a的時候,編譯器會報重定義的錯誤。而在c++中,則不會,因爲c++中的const默認是內部連接的。如果想讓c++中的const具有外部連接,必須顯示聲明爲: extern const int a = 10。
————————————————
原文鏈接:https://blog.csdn.net/woainilixuhao/article/details/86521357

根據資料可知,因爲const修飾後的變量nArrayLength存放在符號表,所以編譯器在編譯期間就知道了數組大小,所以編譯通過。

5)相比於函數調用,爲啥說宏不會帶來額外開銷?

因爲額外開銷指的是開闢棧空間存放參數和返回值。

首先,函數調用會帶來額外的開銷,它需要開闢一片棧空間,記錄返回地址,將形參壓棧,從函數返回還要釋放堆棧。這種開銷不僅會降低代碼效率,而且代碼量也會大大增加,而使用宏定義則在代碼規模和速度方面都比函數更勝一籌;其次,函數的參數必須被聲明爲一種特定的類型,所以它只能在類型合適的表達式上使用,我們如果要比較兩個浮點型的大小,就不得不再寫一個專門針對浮點型的比較函數。反之,上面的那個宏定義可以用於整形、長整形、單浮點型、雙浮點型以及其他任何可以用“>”操作符比較值大小的類型,也就是說,宏是與類型無關的。

6)雖然宏不會帶來額外的開銷,但是有時候容易使用不當,導致不能得到預期結果;並且宏不會檢查參數類型。
eg:

#define CALL_MAX_VALUE(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_MAX_VALUE(++a, b);
// 此時a=7,因爲傳入參數時+1,返回時又+1了

但可以使用template inline函數替代宏:

template<typename T>
    inline T CallWithMax(const T& a, const T& b)
{
     return (a > b ? a : b);
}
從邏輯上來說,編譯器對inline函數的處理步驟一般如下:
(1)將inline函數體複製到inline函數調用處;
(2)爲所用inline函數中的局部變量分配內存空間;
(3)將inline函數的的輸入參數和返回值映射到調用方法的局部變量空間中;
(4)如果inline函數有多個返回點,將其轉變爲inline函數代碼塊末尾的分支(使用GOTO)。

inline函數相對宏函數有如下優點:
(1)內聯函數同宏函數一樣將在被調用處進行代碼展開,省去了參數壓棧、棧幀開闢與回收,結果返回等,從而提高程序運行速度。
(2)內聯函數相比宏函數來說,在代碼展開時,會做安全檢查或自動類型轉換(同普通函數),而宏定義則不會。
————————————————
原文鏈接:https://blog.csdn.net/K346K346/article/details/52065524

可以將內聯理解爲C++中對於函數專有的宏,對於C的函數宏的一種改進。對於常量宏,C++提供const替代;而對於函數宏,C++提供的方案則是inline。並且,inline對聲明沒有作用,必須要在函數的定義時使用inline

總結

關於#define 變量名,由於”***#define 變量名 值***“的變量名是不會出現在符號表,不便於調試,所以用***const 類型 變量名 值***替代。

關於#define 函數,由於#define 函數不會檢查傳入參數的類型以及容易使用不當導致得到結果不合預期,所以用inline替代(在函數定義時使用inline修飾),只有短小代碼段才用inline,不然也很浪費內存~

3. 儘可能用const

1)對指針使用const:

const int * p; // 由於const是修飾*p,所以這個const約束的是p指向的內容,即指針指向的內容

int * const p; // 由於const是修飾p,所以這個const約束的p,即指針的值

2)對迭代器使用const:(迭代器類似指針,但是又不同於指針,所以const對其修飾不能用上面的來理解)

const vector<int>::iterator iter;  // const修飾的是迭代器,所以迭代器不能變

vector<int>::const_iterator iter; // const修飾的是迭代器指向的內容,所以迭代器指向的內容不能變

3)const修飾返回值,使函數返回一個常量值,可以避免出現“想要鍵入==卻鍵成=的錯誤”。

例子:

Rantional a,b,c;

if(a*b = c)
    ...

因爲a、b不是內置類型,所以編譯器不會報錯,導致if並沒有判斷a*b的值是否等於c就直接進入這個條件語句了,導致邏輯出錯。

4)const修飾成員函數:不可以改動對象內容,用於操作處理const對象。
另外,編譯器對const成員函數是執行bitwise constness,即成員函數不能改變對象的任何non-static的成員變量,因爲static變量並不屬於該類了,他是屬於大家的。
通過用mutable修飾變量可以釋放const成員函數的約束。

5)兩個成員函數如果只是常量性不同,那麼可以被重載,eg:

class XX
{
int func() const; // 創建的XX對象是const的,那麼就會調用這個func
int func(); // 創建的XX對象是非const的,那麼就會調用這個func
}
方法重載是指同一個類中的多個方法具有相同的名字,但這些方法具有不同的參數列表,即參數的數量或參數類型不能完全相同.
重載的時候,方法名要一樣,但是參數類型和個數不一樣,返回值類型可以相同也可以不相同。無法以返回型別作爲重載函數的區分標準。

6)用const_cast去掉const,用static_cast加上const,從而實現令non-const版本調用const版本可避免代碼重複性。

eg:

const char& operator[](std::size_t position) const
{
    // 邊界檢驗等其他操作
    ...
    return text[position];
}
char& operator[](std::size_t position)
{
    return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}

Q: 書本提到將“邊界檢驗等其他操作”這部分代碼移到另一個成員函數中,然後兩個版本的operator[]調用它會重複一些代碼,例如函數調用,兩次return語句等。那爲啥這種常量性移除調用[]就避免了代碼重複?

總結

關於const指針, 記住const指針和const迭代器的理解是相反的;
關於const成員函數,當成員函數參數和返回值都一樣,就一個有const,一個沒有const,這也是一種重載;mutable修飾成員變量就可以去掉const成員函數的對該成員變量的約束,PS:const成員函數僅僅約束的是對成員變量的更改。
關於加減const:const_cast是去掉const,static_cast是加上cons。

插播:static_cast常被用作顯式類型轉換使用;const_cast修改類型的const或volatile屬性。(volatile的作用是作爲指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。即volatile的變量是說這變量可能會被意想不到地改變,所以每次都要重新讀值)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章