日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux程序设计知识点整理,笔试面试中C/C++重要知识点整理

發布時間:2023/12/10 linux 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux程序设计知识点整理,笔试面试中C/C++重要知识点整理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

4. ? 類與面向對象編程

4.1 類接口與實現的概念:

每個類都定義了一個接口(可以不是很確切的理解為類中訪問級別為public的函數為接口)和一個實現。接口由使用該類的代碼需要執行的操作組成。實現一般包括該類所需要的數據。實現還包括定義該類需要的但又不供一般性使用的函數。

定義類時,通常先要定義該類的接口,即該類所提供的操作。通過這些操作,可以決定該類完成其功能所需要的數據,以及是否需要定義一些函數來支持該類的實現。

public派生類繼承基類的接口,它具有與基類相同的接口。設計良好的類層次中,public派生類的對象可以用在任何需要基類對象的地方。

4.2 用struct關鍵字與class關鍵定義類以及繼承的區別

(1)定義類差別

struct關鍵字也可以實現類,用class和struct關鍵字定義類的唯一差別在于默認訪問級別:默認情況下,struct成員的訪問級別為public,而class成員的為private。語法使用也相同,直接將class改為struct即可。

(2)繼承差別

使用class保留字的派生類默認具有private繼承,而用struct保留字定義的類某人具有public繼承。其它則沒有任何區別。

class Base{ /*....*/};

struct ?D1: Base{ /*......*/} ; ? ?// 默認是public繼承

class ?D2: Base{/*.......*/}; ?// 默認是private繼承

4.3 類設計與protected成員

可以認為protected訪問標號是private和public的混合:

(1)像private成員一樣,protected成員不能被類的用戶訪問

(2)像public成員一樣,protected成員可以被該類的派生類訪問。

例如:

class Base

{

.........

protected:

int price;

};

class Item_Base :public Base

{

......................

};

Base ? b;

Item_Base d;

b.price; ?// error

d.price: // OK

小結(幫助理解為什么設置protected類型): 如果沒有繼承,類只有兩種用戶:類本身的成員以及該類的用戶,將類劃分為private和public訪問級別反映了用戶類型的這一分割:用戶只能訪問public接口,類成員和友元既能訪問public成員也能訪問private成員。

有了繼承,就有了第三種用戶:從派生類定義新類的程序員。派生類提供者通常(但不總是)需要訪問(類型為private的)基類實現(見4.1實現概念)。為了允許這種訪問而仍然禁止對實現的一般訪問。所以提供了附加的protected訪問標號。類的protected部分仍然不能被一般程序訪問,但可以被派生類訪問。

定義基類時,將成員設置為public的標準并沒有改變:仍然是接口函數應該為public而數據一般不應為public。被繼承的類必須決定實現那些部分為protected哪些部分為private。希望禁止派生類訪問的成員應該設為private,提供派生類實現所需操作或數據的成員設為protected。換句話說,提供給派生類的接口是protected成員和public成員的組合。

4.4 ?派生類與虛函數概述

(1)定義為virtual的函數是希望派生類重新定義。希望派生類繼承的函數不能定義為虛函數。如果派生類沒有重新定義某個虛函數,則在調用的時候會使用基類中定義的版本。

(2)派生類中函數的聲明必須與基類中定義的方式完全匹配,但有一個例外:返回對基類類型的引用(或指針)的虛函數。派生類中的虛函數可以返回基類函數所返回類型的派生類的引用(或指針)。比如:Item_base類可以定義返回Item_base*的函數。如果這樣,派生類Bulk_item類中定義的實例可以定義返回為Item_base*或者Bulk_item*

(3) 一旦函數在基類中聲明為虛函數,它就一直為虛函數,派生類無法改變該函數為虛函數這一事實。派生類重新定義虛函數時,可以使用virtual保留字,也可以省略。

4.5 virtual 函數詳解(待更新)

要觸發動態綁定,必須滿足兩個條件:第一:只有指定為虛函數的成員函數才能進行動態綁定。第二,必須通過基類類型的引用或者指針進行函數調用。下面重點講下第二個條件。

由于每個派生類都包含基類部分,所以可將基類對象引用或者指針綁定到派生類對象的基類部分(派生類對象本身不會改變)。如下:

double print_total(const Item_base&, size_t);

Item_base item;

print_total(item, 10); // OK

Bulk_item bulk;

print_total(bulk, 10); // OK ?引用bulk中Item_base的部分。

Item_base *item = &bulk; // OK , 指針指向bulk 的Item_base部分。

通過引用或者指針調用虛函數時,編譯器將生成代碼,在運行時確定調用哪個函數。比如:

假定print_total 為虛函數,在基類Item_base 和派生類Bulk_item中都有定義。

函數原型:void print_total(ostream& os, const Item_base &item, size_t n);

Item_base base;

Bulk_item derived;

print_total(count, base, 10); // 將調用基類Item_base中的print_total函數

print_total(count, derivede,10); // 將調用派生類中的print_total 函數。

在某些情況下,希望覆蓋虛函數的機制并強制函數使用虛函數的特定版本,這時可以使用作用域操作符。

Item_base *baseP = &derived;

double d = baseP->Item_base::net_price(42);

這段代碼將強制把net_price調用確定為Item_base中版本(在編譯時確定)。

小結:引用和指針的靜態類型與動態類型可以不同,這是C++支持多態性的基石。當通過基類引用或者指針滴哦啊用基類中定義的函數時,我們并不知道執行函數的對象的確切類型,執行函數的對象可能是基類類型的,也可能是派生類類型的。

如果調用非虛函數,則無論實際對象是什么類型,都執行基類中所定義的哦函數。如果調用虛函數,則直到運行時才能確定調用哪個函數。

4.5 派生類到基類的轉換: C++ primer 488 沒有想好怎么整理

4.6 基類與派生類中構造函數和復制控制:

構造函數和復制控制成員不能被繼承,每個類定義自己的構造函數和復制控制成員,如果不定義,則編譯器將合成一個。

繼承對基類中構造函數的唯一影響是,某些類需要只希望派生類使用的特殊構造函數,這樣的構造函數應該定義為protected。

4.6.1 派生類構造函數

派生類構造函數受繼承關系的影響,每個派生類構造函數除了初始化自己的數據成員之外,還要初始化基類。對于合成的派生類默認構造函數,先調用基類的默認構造函數初始化(問題,如果基類沒有定義默認構造函數咋整,要試驗下) 再默認初始化自己的對象成員。。。。。

具體語法參見P491 c++ primer

4.6.2 派生類析構函數

派生類析構函數不負責撤銷基類對象的成員。編譯器總是顯式調用派生類對象基類部分的析構函數。每個析構函數只負責清除自己的成員:

class Derived: public Base

{

// Base::~Base()函數會自動被調用

~Derived();

}

對象的撤銷順序與構造順序相反:首先運行派生類析構函數,然后按照繼承層次依次向上調用各基類的析構函數。

4.6.3 虛析構函數

當闡述指向動態分配對象的指針時,需要運行析構函數在釋放對象之前清除對象。如果把析構函數設置為虛函數,運行哪個析構函數將因指針所指向對象類型的不同而不同:

Item_base *itemP = new Item_base;

delete itemP; ?// ?基類的析構函數被調用

itemP = new Bulk_item;

delete itemP; // 派生類的析構函數被調用

如果不把析構函數定義為虛函數,則會一直調用基類的析構函數,從而引發程序異常。

像其他虛函數一樣,析構函數的虛函數性質將繼承,因此,如果層次中根類的析構函數為虛函數,則派生類析構函數也將是虛函數,無論派生類顯式定義析構函數還是使用合成析構函數,派生類析構函數都是虛函數。

構造函數不是虛函數: 構造函數實在對象完全構造之前運行的,在構造函數運行的時候,對象的動態類型還不夠完整(待理解),所以構造函數不是虛函數。

4.7 繼承情況下的類的作用域

繼承層次中函數調用遵循以下四個步驟:

(1)首先確定進行函數調用的對象,引用或者指針的靜態類型。

(2)在該類中查找函數,如果找不到,就直接在基類中查找,如此循環著類的繼承鏈往上找,直到找到該函數或者查找完最后一個類。如果不能再類或者相關基類中找到該名字,則調用是錯誤的。

(3)一旦找到了該名字,進行常規類型檢查(參數類型檢查等),查看該函數調用是否合法

(4) 假定函數調用合法,編譯器就生成代碼,如果函數是虛函數并且通過引用或者指針調用,則編譯器生成代碼以確定根據對象的動態類型運行哪個函數版本,否則,編譯器生成代碼直接調用函數。

舉例1:

Bulk_item bulk;

cout << bulk.book();

book 的使用將這樣確定:

(1) bulk 是Bulk_item 類對象,在Bulk類中查找,找不到名字book ?( 根據上面第一步,確定靜態類型為Bulk_item, 然后進入第二步)

(2)因為從Item_base派生Bulk_item, 所以接著在Item_base類中查找,找到book ,名字成功確定。

舉例2:

struct Base

{

int menfcn();

};

struct Derived: Base

{

int menfcn(int);

};

Derived d; Base b;

b.memfcn(); ? ? ?// 調用基類的函數

d.menfcn(10); ?// 調用派生類函數

d.menfcn(); ? ? ? // 錯誤:

d.Base::menfcn(); ? // 調用基類函數

第三個調用中出現錯誤,原因是,Derived中的么么fcn聲明隱藏了Base中的聲明。 原因是,根據上面規則,一旦找到了名字,編譯器就不會再繼續查找了。而是進行常規檢查,由于調用與Derived中的memfcn不匹配,該定義希望接受int實參,而這個函數調用沒有提供那樣的實參,所以錯誤

如果派生類重新定義了重載成員,則通過派生類行只能訪問派生類中重新定義的那些成員。

舉例3: 通過基類指針或者引用調用

假定print_total 為虛函數,在基類Item_base 和派生類Bulk_item中都有定義。

函數原型:void print_total(ostream& os, const Item_base &item, size_t n);

Item_base base;

Bulk_item derived;

print_total(count, base, 10); // 將調用基類Item_base中的print_total函數

print_total(count, derivede,10); // 將調用派生類中的print_total 函數。

如果print_total 不是虛函數,根據上面的步驟,將直接調用基類Item_base中的print_total 版本

由于print_total中 第二個參數的靜態類型為Item_base 所以根據規則(1),先Item_base 中查找print_total , 然后進行常規檢查,參數沒有錯,由于函數是虛函數之后根據規則(4),?print_total(count, base, 10);用基類Item_base中的print_total函數,print_total(count, derivede,10);,調用派生類中的print_total 函數。

現在可以理解為什么虛函數在基類和派生類中擁有同一原型了,如果沒有同一原型,比如基類與派生類中參數不同,根據規則3,確定基類中參數沒有問題時,如果根據規則4 實際調用的是派生類中的函數時由于參數不同就會出現錯誤。

4.8 細節知識點

4.8.1 explicit關鍵字

我們可以將構造函數聲明為explicit,來防止在需要隱式轉換的上下文中使用構造函數。例如

class Sales_item

{

public:

Sales_item(const string &book = ""):isbn(book),units_sold(0){}

bool same_isbn(const Sales_item &rhs) const;

};

每個構造函數都定義了一個隱式轉換。因此,在期待一個Sales_item類型對象的地方,可以使用一個string或者istream:如下

string null_book = "9-1111-1111";

item.same_isbn(null_book);

以上程序中,本來程序期待一個Sales_item對象作為實參,編譯器使用接受一個string的Sales_item構造函數從null_book生成一個新的Sales_item對象,新生成的臨時的Sales_item對象被傳遞給same_isbn。

如果我們不想要編譯器隱式的轉換,可以將構造函數聲明為explicit。 注意的是explicit關鍵字只能用于類內部的構造函數聲明上。在類定義外部所作的定義中不再重復它。 比如以下是錯誤的.

explicit Sales_item:: sales_item(istream& is) ? ?//錯誤,explicit只在類內構造函數的聲明上

{

。。。。。。

}

加上explicit關鍵字后,以下就不能編譯通過

item.same_isbn(null_book); // error:string constructor is explicit

當然我們可以顯式的使用構造函數來生成轉換,如下

item.same_isbn(Sales_item(null_book)); ?// OK

總結:通常,除非有明顯的理由需要隱式轉換,否則,構造函數應該為explicit。 將構造函數設置為explicit可以避免錯誤,并且當轉換有用時用戶可以顯式的構造對象

4. 9 虛函數與純虛函數區別

(1)虛函數在子類里面也可以不重載的;但純虛必須在子類去實現

(2)帶純虛函數的類叫虛基類,這種基類不能直接生成對象,而只有被繼承,并重寫其虛函數后,才能使用。這樣的類也叫抽象類。虛函數是為了繼承接口和默認行為

總結

以上是生活随笔為你收集整理的linux程序设计知识点整理,笔试面试中C/C++重要知识点整理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。