在c++中可以取得記憶體位址的、有名字的變數就是左值,左值在使用時可以放在等號左側;右值則是指不能取得記憶體位址的、沒有名字的變數,只能在等號的右側使用。右值直觀上就是臨時變數,如執行a = b + c
時,等號右側優先計算,加和結果會被儲存在臨時變數中,這個臨時變數就是右值。
在c++中,使用左值的方式,可以是通過其「本名」訪問,也可以通過引用訪問,此時使用的引用是左值引用。但是右值沒有關聯名字,為了能夠使用右值,c++引入了右值引用的概念。
void func(string& str); // 這是乙個左值引用
void func(string&& str); // 這是乙個右值引用
問題是我們為什麼要直接使用右值?可以看下面這個例子
struct node
};int input = 10;
node node_a(input);
node node_b(10);
上述例子中,兩個例項的構造過程基本相同;區別在於node_a
中傳入的是左值引用,在使用完後input
變數仍然存活,而node_b
中傳入的是乙個臨時變數,臨時變數的值會被複製到val
中,臨時變數使用完後就被銷毀了。如果傳入的臨時變數空間占用非常大,複製過程就是一筆很大的開銷,何不保留臨時變數並直接將val
指向臨時變數呢?
c++中提供了move
語義,可以將「將亡值」(即將被銷毀的臨時變數)有效期的有效期延長,直接保留臨時變數而避免上述的拷貝過程。
struct node
node(int&& _val)
};int input = 10;
node node_a(input);
node node_b(10);
a = std::move(b)
可以理解為將b
這個名字擦掉,以a
這個名字替換,此時將再也無法使用b
訪問該變數。
使用指標
c++ 提供了 new 和 delete 關鍵字來分配和釋放記憶體(堆)
// 或者直接用 auto
string* p_str = new string; // 這種花括號的使用也是可以的
delete p_str;
使用智慧型指標
智慧型指標是c++為了能夠更安全地使用動態記憶體而產生的一種指標,與傳統指標不同的地方,智慧型指標在不使用時會自動釋放所指向的物件,避免記憶體空間浪費。
智慧型指標使用模板建立:
share_ptrp_int = make_shared(10); // 無須手動釋放
關鍵字 struct 與 class 搜可以由於類的建立,struct 中的所有成員都是公開的,而 class 中的所有成員許可權預設是私有的。
建構函式
預設建構函式
預設建構函式沒有引數,會對類的成員進行預設初始化:如果成員變數有預設初始值,則使用預設初始值,如果成員有預設初始化的方法,則使用這些方法;否則,預設建構函式會報錯。
如果沒有在類中宣告任何建構函式,編譯器會自動建立乙個預設建構函式,但是如果有宣告建構函式,編譯器就不會自動生成預設建構函式了。此時如果需要預設建構函式,可以手工指定classname() = default
。
普通的建構函式
使用帶有引數的建構函式,為類的成員變數賦初始值,以及進行一些其它的初始化動作。這裡可以使用初始值列表:
classname(const type& _a, const type& _b) : var_a(_a), var_b(_b), var_c(0) {}
初始值列表即函式引數之後、函式體之前的部分,還可以在這一部分使用預設初始值來初始化成員變數。
對於類內的 const 成員變數,只能使用初始值列表進行初始化。
使用列表初始化構建類的例項時,會使用該初始值列表,按照成員變數的宣告順序進行初始化操作。
委託建構函式
在建構函式中使用同一類的其它建構函式,就是委託建構函式。委託建構函式的形式:
classname(const type& _a) : classname(_a, 0) {}
這個建構函式使用了上述的帶有初始值列表的建構函式。
轉換建構函式
由於 c++ 提供的隱式轉換,使得建構函式能夠使用的引數型別範圍更大。如果不想要這種隱式型別轉換,可以使用explicit
修飾建構函式,只能使用在宣告處。
拷貝建構函式
拷貝建構函式的引數是類本身,用來實現類的拷貝行為。此外,還有拷貝賦值也實現了類的拷貝,是通過過載=
運算子實現的。
classname(const classname& _c) : var_a(_c.a), var_b(_c.b), var_c(_c.c) {}
classname& operator=(const classname& _c)
classname p1(p2); // 拷貝
classname p1 = p2; // 賦值
如果沒有手工定義拷貝建構函式和拷貝賦值函式,編譯器會自動合成;如果想顯式地要求編譯器合成拷貝建構函式和拷貝賦值函式,使用=default
;如果想禁止類有拷貝的能力,阻止任何的拷貝建構函式和拷貝賦值函式,使用=delete
。
移動建構函式
移動建構函式與拷貝建構函式很類似,用來實現類的移動行為。同樣的,也有移動賦值運算子。
classname(classname&& _c) : /* 這裡不寫了 */ {}
classname& operator=(classname&& _c)
編譯器不會為類合成移動建構函式和移動賦值函式。
析構函式
析構函式用於銷毀例項。如使用delete p
是會呼叫p
的析構函式進行銷毀。
友元友元是用來控制其它的函式或物件,訪問本物件的非公開成員的方法。比如使用列印函式將本物件列印到螢幕,本物件中的某些成員變數是私有的,通過對列印函式新增 friend 字首,使它可以訪問這些私有成員變數。
操作符過載
在類中過載運算子可以豐富類的行為,使類更方便使用。如
過載運算子以符合日常使用的經驗為佳。
虛函式虛函式為允許基類呼叫的派生類的函式。在派生類中不一定要重新定義基類的虛函式,但是重新定義後,基類和派生類就會對該函式擁有各自的版本,在呼叫該函式時動態繫結。借助指標或引用,虛函式能夠實現多型的效果。
class base
}class deriver : public base
}// 此時使用 base 型別的指標定義 deriver 的物件
base* obj = new deriver();
obj.func(); // 此時會使用 deriver 中的 func,而不是 base 中的 func
從上面的例子可以看出,析構函式最好定義成虛函式,否則 obj 可能不能正確地析構。
純虛函式為沒有實現的函式。純虛函式的目的是要求派生類必須實現這一函式,主要是為了規範派生類的功能。帶有純虛函式的類稱為抽象類,因為純虛函式沒有實現,無法進行例項化。
virtual type function(args) = 0; // 純虛函式
物件導向程式設計的三個特徵是:封裝、繼承、多型。
對於 c++ 而言,封裝的概念很好理解:通過類的抽象,許多細節被隱藏在了類的內部,使類的使用者只需要關心介面如何使用即可;以及使用public
,private
,protected
對類成員的許可權控制,使類的使用者不會觸及他們不該訪問的類成員。
c++ 支援單一繼承或多重繼承,提供的抽象類的概念,以及使用public
,private
,protected
進行繼承中許可權的控制,都是對繼承的支援。在繼承中比較容易混淆的是普通成員函式、虛函式、純虛函式,這裡以類指標下的三個函式呼叫說明區別:
class base // 普通函式
virtual string g() // 虛函式
virtual string h() = 0; // 純虛函式
};class deriver : public base
virtual string g()
string h()
};int main()
從上面的比較可以看出:
上面的比較可以看出,使用普通成員函式時,基類更像是多種型別相同點的抽象,使用中直接使用派生類會比較合適(實現繼承)。而使用虛函式和純虛函式時,基類看上去更像是多種派生類的統一介面,通過使用基類呼叫介面相同但行為不同的派生類的功能(介面繼承)。這樣對於同一基類下的派生類,使用基類作為某個函式的引數就能定義出所有派生類的行為,否則就需要每乙個派生類單獨定義。這種方式是多型的體現。
物件導向知識總結(3)
物件導向 的 繼承 封裝 多型 記憶體中用到哪個類才會載入該類的位元組碼檔案進記憶體,沒有執行該類則不載入。super不代表當前父類物件,代表當前父類空間 成員變數 不可以用 abstract修飾,只允許使用 公用 受保護 私有 靜態 終態 瞬態 或 易變 構造器 只能用 public,protec...
Java物件導向知識總結
1形參個數可變的方法 最後乙個引數型別後面的 public static void test int a,string.strings system.out.println a public static void main string args 2.無論是通過類還是通過例項來訪問類變數其實訪問的...
C 物件導向總結
簡答題 1.什麼是類?什麼是物件?物件與類的關係是什麼?答 類就是相同的資料和相同的一組物件的集合,即類是對具有相同資料結構和相同操作的一類物件的描述 物件是描述其屬性的資料以及對這些資料施加的一組操作封裝在一起構成的統一體,物件可以認為是 資料 操作 類和物件之間的關係是抽象和具體的關係。2.建構...