C++88個注意點子之51~70

51. 容器適配器的初始化

stack和queue都默認使用deque容器實現,將deque容器作爲實現的底層數據結構。而priority_queue則在vector上面實現的。那麼,我們如何來修改stack和queue這些適配器的實現結構呢? 比如我們將stack棧適配器用vector動態數組結構來實現,這樣定義: stack< string, vector<string> > strStack;這樣就定義了一個棧結構,元素是string的,然後用vector數組來實現,這裏注意在類型<>中再使用<>時,要注意用空格隔開有可能連在一起的>>,因爲這樣編譯器就不知道該如何解釋了,會與右移運算符搞混。下面來看看stack, queue, priority_queue可以用哪些結構實現:

stack: vector deque(default) list

queue: deque(default) list

priority_queue: vector(default) deque

此外,還有一點需要注意的是,要用基礎容器初始化適配器時,必須和適配器類型相同:

stack<int> b(a); a的類型必須是deque<int>的。


52. pair類型  pair<int, string>

map類型  map<int, string>

map中元素爲pair類型, 初始化中元素類型必須是用pair<k, v>:

map<k, v> m(b, e);


53. 對於map的鍵類型,必須支持'<'運算,若自己定義的類,則需要自己實現<運算。

在map中下標插入元素會帶來不必要的初始化。

吐槽一下C++中的map類,用起來真心沒有java中的map用起來方便。。。


54. 在看別人的源碼時,經常會在程序開頭出現# pragma once 和 # ifndef # endif這些東西。下面梳理一下:

(1) #pragma once 可以保證文件只被包含一次,但相同內容文件則不能保證

(2) #ifndef _ClassName_H

# define _ClassName_H

.....(class define)

#endif

可以保證同一個文件且相同內容文件只被包含一次(受C/C++標準支持)

本人比較喜歡用第二種,但是這兩種方法各有利弊,具體的可以自行了解。


55. 穩定排序與一般排序

在C++中stable_sort()是穩定排序, 而sort()不是穩定的,這兩個函數用法都是一樣的。


56. 在C++類中也可以定義自己的局部類型名字。

public:

      typedef std::string::size_type index;

在public中定義,則不僅類中成員可以使用,類外用戶也可以使用。

也可以定義在private中,僅供類內使用。


57. 在類內部定義的成員函數,將自動作爲inline處理。也可以顯式地將成員函數聲明爲inline;

inline get_cursor() const;

在類外定義: inline 返回類型名 Screen::get_cursor() const;


58. 一般的類定義體都是放在頭文件,用#ifndef保護,然後具體實現放在頭文件外面。


59. 類聲明(前向聲明,在定義前)是不完全類型,只能用於指向該類型的指針及引用,或者用於聲明使用該類型作爲形參類型或返回類型的函數。

class LinkScreen {
      Screen windows;
       LinkScreen *next;
       LinkScreen  *prev;
}

60. 在普通的非const成員函數中,this類型是一個指向類類型的const指針。而在const成員函數中,this是一個指向const類類型對象的const指針。對於返回*this,要格外注意。

Screen& display() {return *this;}

const Screen& display() const {return *this;}

這個兩個是不一樣的函數,重載實現。


61. 構造函數執行分爲二個階段:

(1) 初始化列表初始化 

(2) 構造函數體初始化賦值

對於一般內置類型,初始化列表初始化和賦值性能一樣。

但是對於類類型,初始化階段會調用默認構造函數,再賦值,性能會損失。對於const成員和引用成員一定要在初始化列表中初始化,因爲它們必須在初始化時賦值。

注意:對const或引用或沒有默認構造函數的類類型,使用初始化式。


62. C++中構造函數初始化列表順序無關緊要,重要的是變量定義的次序,按照變量定義順序來初始化。


63. C++中可以用單個實參來調用的構造函數,隱含了從形參類型到類類型的一個隱式轉換。

Sales_item(const std::string &book="");

在外調用含有Sales_item類型的函數時,可以直接傳入一個字符串,編譯器會幫我們自動的用Sales_item單形參構造函數構造出一個匿名類。有的時候這種隱式轉換會導致錯誤。爲了程序的嚴謹性,我們應該用explict關鍵詞來抑制由構造函數定義的隱式轉換。

注意:explict只能用於類內部的構造函數聲明上,在外部定義函數處不能加explict。


64. 除非有明顯的理由想要定義隱式轉換,否則,單形參構造函數應該爲explict。顯示使用構造函數string("hello"), Sales_item("124"), vector<int>(3) , 有點類似於強制類型轉換。


65. 友元,允許一個類將其非公有成員的訪問權授予指定的函數或類。授予類: friend class WindowMgr;

授予函數: friend 返回值 別類::函數();

注意:友元聲明應該成組地放在類定義的開始或結尾。


66. static類型

面向過程式中,

(1) 全局static變量(不能被其它文件使用)

(2) 局部static變量

(3) static 函數(不能被其它文件所用, 同時在其它文件中定義相同名字,不會發生衝突)

面向對象中,

(1) 靜態數據成員, 遵循公有私有等規定用法,不能再構造函數中初始化,需要在類定義外初始化將static去掉。

可用類名直接訪問, Point::size;

(2) 靜態成員函數,無this指針,且只能對static數據成員操作,且能調用其他靜態成員函數,在類體外定義,去掉static名。

注意:static型的數據都存儲在程序的全局數據區中。

(3)特殊的整型const static 成員

必須在類體中初始化,但在類定義體外定義。(無須初始化)

(4) static成員不是類對象的組成部分。對於非static成員被限定聲明爲其自身類對象的指針或引用。而static成員類型可以是該成員所屬的類類型。static數據成員可用作默認實參(在類中)。

我只想說一句:C++中的static規則還真是多啊,要記住不容易,需要多加練習。


67. 複製構造函數在下面幾種情況中被調用:

(1) 對象的定義形式中

(2) 形參與返回值

(3) 數組元素容器初始化

合成複製構造函數:對於類類型調用其複製構造函數,而內建類型逐個成員複製到正創建對象。若爲數組,則依次複製數組中每個元素。

Foo(const Foo&);


68. 可以使用合成複製構造函數的類也可以使用合成賦值操作符。若類需要複製構造函數,它也需要賦值操作符。若類中有指針成員,則一般需要顯式指定複製構造函數和賦值操作符。

複製構造函數: SalesItem(const SalesItems&);

賦值操作符 SalesItem& SalesItems::operator=(const SalesItems &);


69. 析構函數當作用域或指向對象指針刪除時才啓動。注意:如果類需要析構函數,則它也需要賦值操作符和複製構造函數。

與複製構造函數和賦值操作符不同的是編譯器總是爲我們合成一個析構函數,逆序撤銷每個非static成員。即使我們編寫了析構函數,合成析構函數仍然運行。


70.合成複製構造函數默認只複製指針成員的地址,而不會複製指針指向的對象。3種處理辦法:

(1) 指針成員會採用常規指針型行爲。

(2) 類可以實現所謂的"智能指針"行爲。

(3) 類採取值型行爲。(複製指向的對象)

















發佈了65 篇原創文章 · 獲贊 75 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章