1. 程式人生 > >C++期末複習知識點

C++期末複習知識點

1.  面向物件技術有哪些特點?(7條)

a)   模組性:物件是一個功能和資料獨立的單元,相互間只能通過物件認可的途徑進行通訊,可重用。

b)   封裝性:為資訊隱蔽提供具體的實現手段,使用者只需瞭解功能,不必清楚內部細節

c)   程式碼共享:可以避免程式碼的重複設計

d)   靈活性:物件可以根據自身的特點進行功能實現

e)   易維護性:物件實現抽象和封裝後,使可能出現的錯誤基本限制在自身內部,易於檢錯和修改

f)   增量性設計:面向物件系統可以提供繼承機制不斷擴充功能,而不影響原有軟體的執行

g)   區域性儲存與分佈處理:每個物件通過資料抽象和資料隱蔽將其內容和狀態置於自身獨立的儲存結構裡。物件的處理是自治的,由物件構成的系統處理是分散式的

2.  面向物件程式設計與大型程式設計有哪些關係?

•    大型程式是根據待解決問題的複雜度來判定。

–  大型程式必須由多人合作完成,因此進行大型程式設計的管理具有複雜性

–  大型程式有大量的系統狀態,這對測試系統的正確性帶來極大的困難

•    大型程式的實現要求

–  正確性

–  易維護性

–  可讀性

–  可重用性

•    模組分解

–  基於功能的模組分解(橫向)。依據流程圖,以資料為模組的介面

–  基於資料抽象的模組分解(縱向)。依據資訊隱蔽,用資料上的操作為介面

•     軟體系統設計=大型程式設計+小型系統設計

前者解決模組介面複雜,後者控制模組內部的複雜

•    面向物件的設計方法

軟體系統設計=面向物件設計+面向物件程式設計

3.  面向物件設計方法與其它設計方法的比較?

橫向比較:

–  函式程式設計:將計算過程看作函式作用過程

–  邏輯程式設計:將計算過程看作推演過程

–  面向物件程式設計:將計算過程看作分類加狀態變換的過程

縱向比較:

      和結構化程式設計比較。

結構化程式設計強調功能抽象和模組性,將解決問題的過程看作是一個處理過程;而面向物件程式設計綜合了功能抽象和資料抽象,將解決問題的過程看作是一個分類演繹過程。

–  模組與物件:

–  過程呼叫與訊息傳遞:

–  型別與類:

–  靜態連線與動態連線:

4.  解釋以下概念:

物件:物件就是我們認識世界的基本單元,整個世界就是形形色色的物件組成。它是邏輯實體,包括資料和完成處理所需要的程式碼。程式=物件+訊息

訊息:就是物件之間相互請求或相互協作的途徑。訊息只說明操作的功能而不說明操作如何實現

封裝:物件是類的例項,物件的所有資料和對這些資料的操作封裝在一起,外部物件只有通過給它發訊息來改變或得到這些資料。

協議:協議由一個物件能夠接受並且願意接受的所有訊息構成的對外介面。其它物件只能向該物件發協議中所提供的訊息;

類:對一組相似物件的共同抽象描述,它將該組物件所具有的的共同特徵(包括操作特徵和儲存特徵)結合在一起,用於說明該組物件的能力和性質。

繼承:是一種現實世界物件之間獨特的關係,它使得某類物件可以繼承另外一類物件的特徵和能力.

5.  物件的特點?(5條)

答:自主性:物件具有處理能力

•     封閉性:物件具有資訊隱蔽能力

•     互動性:物件之間具有通訊能力

•     被動性:物件的處理工作是由外部物件傳送訊息來啟動

•     動態性:物件可以動態建立和取消,物件狀態是不斷變化的

  五大特徵中,前三項是物件的能力,被動性刻化了物件的活動特性,動態性指出了物件的生存特性。

6.  什麼是例項?

答:任何單個物件都是某個類的例項。一個類的所有例項都採用同樣的方法處理訊息,但每個例項又有自己的私有儲存單元

類與例項的關係是什麼?

答:是抽象和具體的關係:

–  類的所有例項能響應的訊息模式相同,且採用同樣的方法完成訊息所要求的操作

–  類的所有例項具有相同的資料結構,且採用相同的名字來引用

        例項是類的具體化

7.  物件間的關係有哪幾種?

答:物件間的建立關係:一個物件可以通過方法建立一個或多個物件

物件間的聚合關係:實際是一種包含關係

物件通訊關係:訊息流圖描述系統中物件間的訊息流,外向訊息流,內向訊息流

例項化關係:物件是一個類的例項

8.  解釋以下概念:

多型:意味著一個物件有著多重特徵,可以在特定的情況下,表現不同的狀態,從而對應著不同的屬性和方法。多型即一名多用,也即同一訊息可以根據傳送訊息物件的不同採用多種不同的行為方式。

作用域和語景:作用域和語景是對同一問題的兩個不同觀點。

–  作用域是限定一個名字的可用性程式碼範圍,就是這個名字的作用域,提高程式邏輯的區域性性,增強程式的可靠性,減少名字衝突

–  語景是程式中某處所有可訪問物件的集合

深拷貝:是得到一個物件的副本的操作,有新物件產生

淺拷貝:只複製物件的基本型別,物件型別,仍屬於原來的引用,是得到一個物件的指標的操作

9.  強型別與弱型別的區別在哪?它們的代表語言?

弱型別系統的目標是讓所有資訊的語景儘可能一般化,而強型別系統的目標是讓所有資訊的語景盡能特殊化。編譯時,弱型別系統不做變數的型別檢查,到執行時才進行型別匹配;強型別則要對所有變數和表示式進行型別匹配檢查。

優缺點:弱型別使得系統非常靈活,且易擴充,但又易導致草率編碼,還會導致整個程式很難高效。強型別依靠編譯器檢查程式碼的合法性,程式的所有程式碼操作的資料都有清晰的定義,但其生成的程式靈活性差不易修改。

區別:強型別,意味著必須事先宣告變數,並且該變數只可用於表示一種型別的資料(例如或者一個整數或者一個字串)。弱型別,即一個變數不必宣告其型別,一個被用於表示字串的變數在後面的程式中也可用來表示數字;

代表語言:C ,c#,java是強型別語言,VBScript, PHP弱型別語言

10. 引用:引用就是某一變數(目標)的一個別名,對引用的操作與對變數直接操作完全一樣。引用必須要初始化。

int a; int &ra=a; //定義引用ra,它是變數a的引用,即別名

ra=1; 等價於 a=1;

且不能再把該引用名作為其他變數名的別名。它本身不是一種資料型別,引用本身不佔儲存單元

不能建立陣列的引用。因為陣列是一個由若干個元素所組成的集合,所以無法建立一個數組的別名

常引用宣告方式:int a ;

constint &ra=a;// 不能通過引用對目標變數的值進行修改

ra=1; //錯誤

a=1; //正確

假設有如下函式宣告:

stringfoo( );

voidbar(string & s);

  那麼下面的表示式將是非法的:

bar(foo());

bar("helloworld");

  原因在於foo( )和"hello world"串都會產生一個臨時物件,而在C++中,這些臨時物件都是const型別的。因此上面的表示式就是試圖將一個const型別的物件轉換為非const型別,這是非法的。

const:常量限定修飾符。const物件必須初始化而且是在定義的同時。初始化後的const物件(或指標)是不能修改的。

可修飾常數量、變數(包括指標常量和引用變數)和函式。

常量指標:intconst *p; 或 const int *p 指標p可變,指標指向的值*p不可變

指標常量:int *const p 指標p不可變,指標所指向的值*p可變

指向常量的常量指標:int const *const p

const修飾成員函式, 這個成員函式不會改變類的狀態(即類的私有資料) int Fun() const;

靜態成員:是那些與類本身有關的成員資料和成員函式,而不是與該類物件相關的成員資料和成員函式。

靜態資料成員只有一個例項存在,靜態方法與類相關

靜態資料成員不能在類中初始化,也不能在類的建構函式中初始化該成員

一般形式:

    資料型別類名::靜態資料成員名=初值 如:inttest::num = 10;

注意:不能用引數初始化表對靜態成員初始化。一般系統預設初始為0。

靜態成員函式不能呼叫非靜態資料成員,要通過類的物件來呼叫,非靜態成員函式可以任意地訪問靜態成員函式和靜態資料成員

靜態成員函式在類外實現時不能加static關鍵字,否則是錯誤的

int test::Getnum()

{

       .........

}

靜態成員仍然遵循public,private,protected訪問準則

靜態成員函式沒有this指標,它不能返回非靜態成員,因為除了物件會呼叫它外,類本身也可以呼叫。

呼叫靜態成員函式,可以用成員訪問操作符(.)和(->)為一個類的物件或指向類物件的指標呼叫靜態成員函式

11. 友元:為了使其他類的成員函式直接訪問該類的私有變數。允許外面的類或函式去訪問類的私有變數和保護變數,從而使兩個類共享同一函式。

下面兩種情況需要使用友元函式:(1)運算子過載的某些場合需要使用友元。(2)兩個類要共享資料的時候

友元函式沒有this指標,友元函式不能被繼承

運算子過載:C++中預定義的運算子的操作物件只能是基本資料型別,對於許多使用者自定義型別(例如類),也需要類似的運算操作

(1) 除了類屬關係運算符"."、成員指標運算子".*"、作用域運算子"::"、sizeof運算子和三目運算子"?:"以外,C++中的所有運算子都可以過載。

(2)運算子過載實質上是函式過載,因此編譯程式對運算子過載的選擇,遵循函式過載的選擇原則。

(3)過載之後的運算子不能改變運算子的優先順序和結合性,也不能改變運算子運算元的個數及語法結構

(4)運算子過載不能改變該運算子用於內部型別物件的含義。它只能和使用者自定義型別的物件一起使用,或者用於使用者自定義型別的物件和內部型別的物件混合使用時

(5)過載的功能應當與原有功能相類似,避免沒有目的地使用過載運算子

運算子函式過載一般有兩種形式:過載為類的成員函式和過載為類的非成員函式。非成員函式通常是友元。

(1) 一般情況下,單目運算子最好過載為類的成員函式;雙目運算子則最好過載為類的友元函式。

(2)以下一些雙目運算子不能過載為類的友元函式:=、()、[]、->。

>>、<< 過載為友元函式

(3)若一個運算子的操作需要修改物件的狀態,選擇過載為成員函式較好。

(4)當需要過載運算子具有可交換性時,選擇過載為友元函式

12.函式過載的概念

指函式名相同,但是它的引數表列個數或順序,型別不同。但是不能靠返回型別來判斷

13.   建構函式:作用:初始化物件的資料成員

有引數表,可以過載。無返回值,不能繼承

         預設建構函式可以有形參,沒有實參

解構函式:主要是釋放物件佔有的資源。一個類最多隻能有一個解構函式,無引數,無返回值

初始話列表:初始化列表在建構函式主體開始執行前初始化成員物件。它的作用是改善程式的效能。而且類裡的引用資料成員必須在初始化列表裡初始化。

class CExample {
public:
    int a;
    float b;
    //建構函式初始化列表    CExample(): a(0),b(8.8)
    {}
    //建構函式內部賦值    CExample()
    {
        a=0;
        b=8.8;
    }
};

14. 公有繼承的含義(替代原則Liskov Substitution Principle)

公有繼承意味著“is a kind-of”的關係,簡稱isa。class D : public B,這就是告訴大家每個型別為D的物件也是一個型別為B的物件,反之不然。也就是說“B物件派得上用場的任何對方,D物件也可以派上用場”

15.C++幾種繼承方式的比較

1. 公有繼承(public)

公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問。

2. 私有繼承(private)

私有繼承的特點是基類的公有成員和保護成員都作為派生類的私有成員,並且不能被這個派生類的子類所訪問。

3. 保護繼承(protected)

保護繼承的特點是基類的所有公有成員和保護成員都成為派生類的保護成員,並且只能被它的派生類成員函式或友元訪問,基類的私有成員仍然是私有的。

下面列出三種不同的繼承方式的基類特性和派生類特性。

class B :public A{};

public

protected

private

共有繼承

public

protected

不可見

私有繼承

private

private

不可見

保護繼承

protected

protected

不可見

16. 虛擬函式:被virtual關鍵字修飾的成員函式,虛擬函式的作用是實現多型性(Polymorphism),虛擬函式必須實現。純虛擬函式:virtualReturnType Function()=0;  在派生類中必須給予重寫以實現多型性,含有純虛擬函式的類稱為抽象類,不能生成物件。

函式覆蓋:發生在父類與子類之間,其函式名、引數型別、返回值型別必須同父類中的相對應被覆蓋的函式嚴格一致,覆蓋函式和被覆蓋函式只有函式體不同。基類函式必須有virtual關鍵字

動態繫結動態繫結是將一個過程呼叫與相應程式碼連結起來的行為。是指與給定的過程呼叫相關聯的程式碼,只有在執行期才可知的一種繫結,他是多型實現的具體形式。

17.函式過載和覆蓋的比較

函式過載是指函式名相同,而函式的引數個數或型別不同;

覆蓋是指在派生類中成員函式與基類成員函式的函式名、引數個數、型別與返回值均相同;

C++中正是通過虛擬函式的覆蓋,實現多型的功能。

a.成員函式被過載的特徵:
(1)相同的範圍(在同一個類中);
(2)函式名字相同;
(3)引數不同;
(4)virtual 關鍵字可有可無。
b.覆蓋是指派生類函式覆蓋基類函式,特徵是:
(1)不同的範圍(分別位於派生類與基類);
(2)函式名字相同;
(3)引數相同;
(4)基類函式必須有virtual 關鍵字。

18. 抽象基類: 就是類裡定義了純虛成員函式的類

19. 虛基類:派生類虛繼承它的時候,它叫做虛基類,本身可以是普通類,解決多重多級繼承造成的二義性問題

20. 虛繼承: 解決多繼承出現重複的基類這種情況,

在任何派生類中的virtual基類總用同一個(共享)物件表示

class FinalClass: virtual public MakeFinal{}

21. 異常: (exception)是程式可能檢測到的,執行時刻不正常的情況(除0、陣列越界訪問、記憶體耗盡等),出現這些情況希望程式立即處理。C++提供一些內建的語言特性來產生並處理異常,這些語言特性會啟用一種執行時刻的機制(異常處理機制),提供這種機制可以在程式的兩個無關的部分進行異常通訊

異常處理機制是一種非區域性的控制結構,基於棧回退(stack unwiding),因此也可以看做另一種返回機制

如沒有匹配的catch子句處理該異常,程式的執行權將交給C++標準庫定義的terminate()。

名字空間基本概念: 實際上就是一個由程式設計者命名的記憶體區域,程式設計者可以根據需要指定一些有名字的空間域,把一些全域性實體分別放在各個名稱空間中,從而與其他全域性實體分隔開來。

namespace ns1 //指定命名中間nsl

      { int a;

        doubleb; }

程式中要使用變數a和b,nsl::a,nsl::b

名稱空間裡可以包含:變數(可以帶有初始化);常量;數(可以是定義或宣告);結構體;類;模板;名稱空間(巢狀名稱空間)

在宣告類時在右花括號的後面有一分號,而在定義名稱空間時,花括號的後面沒有分號。

22. 模板基本概念: 模板是實現程式碼重用機制的一種工具,它可以實現型別引數化,即把型別定義為引數,從而實現了真正的程式碼可重用性。

支援引數化多型的工具

使用模板的目的就是能夠讓程式設計師編寫與型別無關的程式碼。

模板的宣告或定義只能在全域性,名稱空間或類範圍內進行。不能在區域性範圍,函式內進行

比如swap的模板函式形式為 template <class T> voidswap(T& a, T& b){}

一但聲明瞭類模板就可以用類模板的形參名宣告類中的成員變數和成員函式,即可以在類中使用內建型別的地方都可以使用模板形參名來宣告。比如

template<class T> classA{public: T a; T b; T hy(T c, T &d);};

在類A中聲明瞭兩個型別為T的成員變數a和b,還聲明瞭一個返回型別為T帶兩個引數型別為T的函式hy。

23. 設計模式概念:設計模式使人們可以更加簡單方便地複用成功的設計和體系結構

Abstract Factory(抽象工廠,物件建立型模式):提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。

Adapter(介面卡,類物件結構型模式):將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。

Singleton(單例,物件建立型模式):保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

Iterator(迭代器,物件行為型模式):提供一種方法順序訪問一個聚合物件中各個元素, 而又不需暴露該物件的內部表示。

Proxy(代理,物件結構型模式):為其他物件提供一個代理以控制對這個物件的訪問。

Singleton模式的C++實現

懶漢式,訪問量較小時用,以時間換空間

classCSingleton

{

private:

    CSingleton(){};

    staticCSingleton * m_pInstance;

public:

staticCSingleton* GetInstance()

{

     if( m_pInstance == NULL ) 

         m_pInstance= newCSingleton();

     returnm_pInstance;

}

};

餓漢式,執行緒安全,空間換時間,訪問量比較大時用

classCSingleton 

private: 

    CSingleton()   

    { 

    } 

public: 

    staticCSingleton * GetInstance() 

    { 

        staticCSingleton instance;  

        return&instance; 

    } 

};

class Complex

{

public:

 Complex( double real, double imaginary = 0 )錯誤[1],見下面1

    :_real(real), _imaginary(imaginary)

  {

  }

 void operator+ ( Complex other ) 錯誤[2],(此處存在兩個問題)見下面2。

  {

   _real = _real + other._real;

   _imaginary = _imaginary + other._imaginary;

  }

 void operator<<( ostream os ) 錯誤[3],見下面3

  {

   os << "(" << _real << "," <<_imaginary << ")";

  }

 Complex operator++()錯誤[4],見下面4

  {

   ++_real;

   return *this;

  }

 Complex operator++( int ) 錯誤[5],見下面5

  {

   Complex temp = *this;

   ++_real;

   return temp;

  }

private:

 double _real, _imaginary;

};

………………………………….…修改………………………………………….

class Complex

{

public:

explicit Complex( double real, doubleimaginary = 0 ) [1]此處建構函式加explicit,通知編譯器不提供隱式轉換。

    :real_(real), imaginary_(imaginary)

  {

  }

 Complex& operator+=( const Complex& other )  [2]要考慮使用者的直覺,一般用+=去實現+,

  {

   real_ += other.real_;

   imaginary_ += other.imaginary_;

   return *this;

  }

 Complex& operator++() [4]此處把返回值型別改為引用,是為了與return *this匹配。

  {

   ++real_;

   return *this;

  }

const Complex operator++( int )

[5]此處加const(使使用者不能修改返回的引數),主要是為避免使用者無意去修改,出現i++++這種連加的情況。

  {

   Complex temp( *this );

   ++*this;

   return temp;

  }

 ostream& Print( ostream& os ) const

  {

   return os << "(" << real_ << ","<< imaginary_ << ")";

  }

 friend const Complex operator+( const Complex& lhs, constComplex& rhs );[2]+必須過載為友元函式

 friend ostream& operator<<( ostream& os, constComplex& c );

 [3]對於二元運算子(運算元有兩個)如:+,<<,通常過載為友元函式而不是過載為成員函式。具體函式的實現在類外實現。

 private:

   double real_, imaginary_;

  };

 const Complex operator+( const Complex& lhs, const Complex& rhs)

  {

   Complex ret( lhs );

   ret += rhs;//會自動呼叫Complex& operator+=( const Complex& other ),實現兩複數加。

   return ret;

  }

 ostream& operator<<( ostream& os, const Complex& c )

  {

   return c.Print(os);

}

2

class Array

{

private:

       intptr[100];

public:

       Array(constArray &init);

       ~Array();

};

Array::~Array() //解構函式

{

              delete  ptr; 此處錯誤,應改為delete []ptr,因為ptr型別為陣列。

}

3、

Class Base

{

Public:

 Base();

 ~Base();此處錯誤,應改為virtual ~Base(),用於基類指標來釋放派生類物件時,為了能調到派生類的解構函式。如果此處不加虛解構函式,會造成派生類的解構函式調不到,從而可能出現記憶體釋放不掉。

}

Class A:public Base

{

 public:

 A();

 ~A();

}

void main()

{

   Base * p=new A;//使基類指標指向派生類物件

       deletep;//通過釋放基類指標來釋放派生類物件

}