C++ 大總結

繼C語言之後,C++也告一個段落了,不過,只是總要串起來才能更有效的掌握,那麼現在就來再回顧一下C++的內容,並且將其串起來。

在學習C++之前,我們就用C語言實現了順序表和鏈表,這是學習數據結構的基礎,提前接觸總是好的,在學習c++之後,我們又實現了了兩次,分別是使用類實現、使用模板類實現。那麼接下來我們就進入C++的第一個主題——類

說到類,我就不得不提結構體,當然不是C語言中的結構體,而是C++中的結構體,想想這兩者之間的不同:C++中的結構體不僅僅可以存放數據,而且還可以存放函數,不過,我們通常喜歡使用另一個關鍵字——class

那麼現在就再來回顧一下類的定義吧:

class ClassName
{
    //……
};

class爲定義類的關鍵字,ClassName爲類的名字,{}中爲類的主體,注意類定義結束時後面分號。類中的元素稱爲類的成員;類中的數據稱爲類的屬性或者類的成員數據;類中的函數稱爲類的方法或者成員函數。

http://blog.csdn.net/jhcconan614/article/details/55253501

當時在總結類的時候總結了類的留個成員函數,那麼我們再來回顧一下,它們分別是:

類的構造函數、析構函數、拷貝構造函數、賦值運算符 重載、取地址運算符重載、const修飾的取地址運算符重載。那麼我們重點說一下前幾個

類的構造函數:是一個特殊的成員函數,名字與類名相同,創建類類型對象時,由編譯器自動調用,在對象的生命週期內只且只調用一次,以保證每個數據成員都有一個合適的初始值。

構造函數的特性:
1、函數名與類名相同。
2、沒有返回值。
3、有初始化列表(可以不用)。
4、新對象被創建,由編譯器自動調用,且在對象的生命期內僅調用一次。
5、構造函數可以重載,實參決定了調用那個構造函數。
6、如果沒有顯式定義時,編譯器會提供一個默認的構造函數。
7、無參構造函數和帶有缺省值得構造函數都認爲是缺省構造函數,並且缺省構造函數只能有一個。

類的析構函數:與構造函數功能相反,在對象被銷燬時,由編譯器自動調用,完成類的一些資源清理和汕尾工作。

析構函數的特性:
1、析構函數在類名(即構造函數名)加上字符~。
2、析構函數無參數無返回值。
3、一個類有且只有一個析構函數。若未顯示定義,系統會自動生成缺省的析構函數。
4、對象生命週期結束時,C++編譯系統系統自動調用析構函數。
5、注意析構函數體內並不是刪除對象,而是做一些清理工作。

類的拷貝構造函數:只有單個形參,而且該形參是對本類類型對象的引用(常用const修飾),這樣的構造函數稱爲拷貝構造函數。拷貝構造函數是特殊的構造函數,創建對象時使用已存在的同類對象來進行初始化,由編譯器自動調用。

在這一個模塊裏有一個重要的地方,那就是淺拷貝問題,這個在之前也總結過,所以大致回顧一下:

淺拷貝的問題是怎樣出現的?又怎麼解決這個問題呢?

淺拷貝是經過拷貝之後,兩個對象公用一個內存空間,導致釋放對象的時候出現內存泄漏的問題。解決的方案有三個,分別是深拷貝的普通版本和簡化版本還有寫時拷貝。

深拷貝和寫實拷貝的方法不同:深拷貝是再開闢一段空間放置拷貝的對象,這樣從根本上解決問題,沒有出現兩個對象共用一塊內存,就不會出現內存泄露的問題;而寫拷貝是通過計數器的形式來將這塊內存空間的使用對象個數寫出來,這樣釋放的時候,通過判斷計數器中的數字是不是0來決定是否釋放空間。

還有一些點,我通過畫圖的形式來看一下:

這裏寫圖片描述

內存管理

在C語言中申請空間是通過malloc、colloc、realloc在堆上申請的,釋放空間是通過free。那麼在C++中又有什麼呢?

在C++中申請空間我們一般使用new,釋放空間我們使用delete;如果要申請一組空間,我們使用new[],同樣與之匹配的是delete[]。

重點:new和delete、new[]和delete[]一定匹配使用,否則可能出現內存泄露甚至崩潰的問題。

malloc/free和new/delete的區別:

  1. 它們都是動態管理內存的入口。
  2. malloc/free是C/C++標準庫的函數,new/delete是C++操作符。
  3. malloc/free只是動態分配內存空間/釋放空間。而new/delete除了分配空間還會調用構造函數和 析構函數進行初始化與清理(清理成員)。
  4. malloc/free需要手動計算類型大小且返回值會void*,new/delete可自己計算類型的大小,返回 對應類型的指針。

定位new表達式:定位new表達式是在已分配的原始內存空間中調用構造函數初始化一個對象。

繼承

1、首先說一下什麼是繼承:繼承是可以複用的最重要的手段,它允許程序員在保持原有類特性的基礎上進行擴展和增進功能。這樣產生的新類成爲派生類。

2、那麼接下來的重點就是繼承體系中的基類和派生類了,這兩者是繼承關係,那麼就有繼承權限了,繼承權限有三種,分別是:public、private、protected。其中public用的最多,在實際運用中使用的都是public。

3、我們在學習類的時候其實學了類的訪問權限,也是三個:public、private、protected。在學習繼承之前,後兩者是沒有什麼區別的,但是,學習了繼承之後,這兩個就有區別了,可以說,protected就是爲了繼承體系而生的。那麼我們來看一下他們的區別吧:

基類的private成員在派生類中是不能被訪問的,如果基類的成員不想被類外直接訪問,但需要在派生類中被訪問,就定義protected。

4、然後再來說一下我們在基類和派生類中發現的一個現象,就是同名隱藏了,這和函數重載相似但是不同,首先一個重要的不同點就是作用域不同,同名隱藏發生在繼承體系中,基類的一個成員函數和派生類的一個函數的名稱相同,然後基類的成員函數在訪問的時候就會被隱藏(只是藏起來了,並沒有消失)。

既然同名隱藏(重定義)的兩個函數的作用域不相同,那麼顯然就不會構成重載

5、之後就進入這一個模塊的額重點了——對象模型

對象模型其實就是類的成員在內存的佈局,想要了解繼承到底是怎樣進行的,首先就要知道各個成員在內存中是怎麼分佈的。

6、對了還有一點,就是友元關係可以繼承嗎?答案是否定的,因爲友元函數不是類的成員函數。

7、上面的一些理論都是基於單繼承說的,有單繼承同樣也有多繼承,說到多繼承就不得不再提對象模型了。涉及多繼承那麼顯然繼承的基類不止一個,那麼,其內存空間又是怎樣的佈局呢?或者說,繼承的先後順序是什麼。經實驗驗證,繼承的順序是按照寫的先後順序執行的。那麼在空間裏也就是這樣分佈的。

8、菱形繼承:顯而易見,是繼承體系呈現的是菱形,如圖:

這裏寫圖片描述

http://blog.csdn.net/jhcconan614/article/details/56685420

多態

1、多態的分類:多態分爲靜態多態和動態多態。

靜態多態是編譯完之後確定所要完成的工作,如:函數重載和泛型編程

說到動態多態就不得不提動態綁定的條件:一是虛函數,基類中必須有虛函數,在派生類中必須重寫虛函數;二是,通過基類類型的指針或引用來調用虛函數。

2、說到重寫,顧名思義就是將函數重新寫一遍,但是也有要求,必須是一個在基類中的虛函數,那麼在派生類中必須重寫該函數,重寫的函數和基類的函數原型相同,不過有一個特例——協變,是重寫的特例,基類中返回值是基類類型的引用或指針,在派生類中,返回值爲派生類類型的引用或指針。

說到這裏,回想起來繼承體系中也有函數原型相同的,一個是函數重載,一個是同名隱藏,那麼接下來我們就來看一下他們的異同:

這裏寫圖片描述

3、前面說的虛函數都是在普通的成員函數中定義的,那麼,現在我們來看幾個特殊的成員函數:

構造函數可以定義成虛函數嗎?爲什麼?

構造函數的作用我們都知道,是創建對象的,而虛函數的調用是通過對象來進行的,這是矛盾的,所以說構造函數不能聲明成虛函數。

靜態成員函數可以定義成虛函數嗎?爲什麼?

如果定義成靜態成員函數,那麼在這個函數可以通過類名和域作用符來調用,也就是說,不用創建對象就可以調用。虛表地址的使用必須通過對象的地址才能獲取。

析構函數可以定義成虛函數嗎?爲什麼?

如果我們定義一個Base*類型的變量,但是調用的是Derived的構造函數,那麼之後清理資源的時候,會出現什麼問題呢?

我們會發現,調用析構函數的時候只會調用Base類的析構函數,而不會調用Derived的析構函數,這樣就會引起內存泄漏,所以我們將析構函數定義成虛函數,就會解決這個問題。

4、虛表指針——>虛表

http://blog.csdn.net/jhcconan614/article/details/60879226

模板

之前說靜態多態的時候舉了兩個例子,一個是函數重載,另一個是泛型編程,函數重載我們都清楚,並且還和重定義、重寫一起總結過,但是這個泛型編程是什麼?

編寫與類型無關的邏輯代碼,就是泛型編程,而模板就是泛型編程的基礎。

模板分爲兩部份,一部分是模板函數,另一部分是模板類。

首先說一下模板函數,其格式是:

template<typename Param1,typename Param2,…>
返回值類型 函數名(參數列表)
{
……
}

2、實例化,當你寫一個通用的加法的時候,如果將通用類型寫成T,那麼在來調用函數的時候,通過實例化類型來達到計算任意類型的數據的目的,如果你將T實例化成int類型的那麼結果也會是int的,同樣的,如果將結果實例化成float、char等也會有相應類型數據的結果。

在模板函數推衍過程中不會進行隱式的類型轉化

3、模板形參

模板形參在模板形參列表中只能出現一次

模板形參可以是類型形參也可以是非類型形參,所有類型形參前面必須加上class或者typename關鍵字修飾

定義模板函數時,模板形參列表不能爲空

模板類型形參可作爲類型說明符用在模板中的任何地方,與內置類型或自定義類型
使用方法完全相同,可用於指定函數形參類型、返回值、局部變量和強制類型轉換

在函數模板的內部不能指定缺省的模板實參

顯式指定一個空的模板實參列表,該語法告訴編譯器只有模板才能來匹配這個調用,而且所有的模板參數都應該根據實參推演出來

4、模板函數的特化:模板函數雖然很強大,可以將代碼寫完之後帶入類型,然後運算該類型的數據,但是並不是所有的類型都適合。我們寫的加法的通用函數模板不是不能計算字符串嗎?

那麼該如何解決呢?當然是模板函數的特化了:

模板函數特化形式如下:
1.關鍵字template後面接一對空的尖括號<>
2.函數名後接模板名和一對尖括號,尖括號中指定這個特化定義的模板形參
3.函數形參表
4.函數體

5、模板類

1.在使用類模板時,需要我們顯示的給出模板實參列表,否則編譯器無法得知實際的類型。

2.類模板的成員函數可以在類模板的定義中定義(inline函數),也可以在類模板定義之外定義(此時成員函數定義前面必須加上template及模板參數)。

3.類模板成員函數本身也是一個模板,類模板被實例化時它並不自動被實例化,只有當它被調用或取地址,才被實例化。

http://blog.csdn.net/jhcconan614/article/details/62068169

異常處理

1、C語言異常處理的幾種方式:

1.終止程序(除數爲0)
2.返回一個表示錯誤的值,附加錯誤碼(GetLastError())
3.返回一個合法值,讓程序處於某種非法的狀態(坑爹的atoi())
4.調用一個預先準備好在出現”錯誤”的情況下用的函數(回調函數)。
5.暴力解決方式:abort()或者exit()
6.使用goto語句
7.setjmp()和longjmp()組合

2、按類型捕獲:異常是通過拋出對象引發的,該對象的類型決定該激活哪一個處理代碼。被選中的處理代碼是調用鏈中與該類型匹配且離拋出 對象最近的那一個

拋出異常後會釋放局部存儲對象,所以被拋出的對象也就還給系統了,throw表達式會初始化一個拋出特殊的異常對象副本(匿名對象),異常對象由編譯管理,異常對象在傳給對應的catch處理之後撤銷。

3、棧展開:棧展開就是沿着調用鏈查找匹配的catch語句的過程。

刨出異常的時候將暫停當前函數的執行,開始查找匹配的catch子句。首先查找throw本身是否在try塊內部如果是再查找匹配的catch子句;如果有匹配的則處理,沒有則推出當前函數棧,繼續在函數的棧中進行查找。不斷重複上述的過程。如果到main函數的棧,還沒有找到,那麼終止程序。

4、有時候一個catch子句不能處理一個異常,這時候,就會將這個異常重新拋出,交給更外層的調用鏈處理

5、異常規範

異常對象的類型與catch說明符的類型必須完全匹配。只有以下幾種情況例外
1、允許從非const對象到const的轉換。
2、允許從派生類型到基類類型的轉換。
3、將數組轉換爲指向數組類型的指針,將函數轉換爲指向函數類型的指針。

不要再構造函數和析構函數中拋出異常,否則可能會引起對像的不完整,導致資源泄露

http://blog.csdn.net/jhcconan614/article/details/68958362

智能指針

說到智能指針我們就會想到幾個:auto_ptr、scoped_ptr、shared_ptr

1、那麼我們今天在來回顧一下這幾個智能指針,首先說一下auto_ptr,auto_ptr在庫中是建議不要使用的,因爲它有問題:

一是,已經轉移權限的指針要是再次被使用的時候會出現錯誤,因爲那個指針在轉移資源之後就被賦成空指針了,要是再使用,通過解引用的方法來賦值 ,顯然是不行的,所以就報錯了。

二是,拷貝運算符接受了常量。我們知道臨時對象具有常性,(也就是不能改變),如果使用拷貝運算符的時候,將一個臨時對象附進去,也就是像例子中的那樣,那麼就會出錯。

2、說到scoped_ptr,就不得不說它的特點,那就是霸道,因爲,它不允許別人和他共用一塊內存空間,也就是不允許拷貝。那麼問題來了,如何防止拷貝

記得以前我們給出了三種方法,再來看一下吧:

1、在類中給出賦值運算符的重載和拷貝構造函數的聲明,但是,不給出定義;

2、在類中給出賦值運算符的重載和拷貝構造函數的定義,不過給成私有的;

3、在類中 只 給出賦值運算符的重載和拷貝構造函數的聲明,並且給成私有的。

說實話,這三種方案現在再來看,只有第三種還可行,因爲現在再來看的話,前兩種漏洞很容易就會看出,第一種,因爲是public的,我們可以在類外將這個函數實現了;第二種,不能防友元。

http://blog.csdn.net/jhcconan614/article/details/69755273

shared_ptr:定製刪除器 和 循環引用
http://blog.csdn.net/jhcconan614/article/details/70162257

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