1. 程式人生 > >C++中的基類與派生類

C++中的基類與派生類

派生類的繼承方式總結:

繼承方式 說明
public 基類的public和protected的成員被派生類繼承後,保持原來的狀態
private 基類的public和protected的成員被派生類繼承後,變成派生類的private成員
protected

基類的public和protected的成員被派生類繼承後,變成派生類的protected成員

注:無論何種繼承方式,基類的private成員都不能被派生類訪問。從上面的表中可以看出,宣告為public的方法和屬性可以被隨意訪問;宣告為protected的方法和屬性只能被類本身和其子類訪問;而宣告為private的方法和屬性只能被當前類的物件訪問。

1. 友元函式必須在類中進行宣告而在類外定義,宣告時須在函式返回型別前面加上關鍵字friend

。友元函式雖不是類的成員函式,但它可以訪問類中的私有和保護型別資料成員。

2. 虛擬函式在重新定義時引數的個數和型別必須和基類中的虛擬函式完全匹配,這一點和函式過載完全不同。

3. #include <檔名>和#include "檔名"

  • 檔案包含的兩種格式中,第一種格式用來包含那些由系統提供的並放在指定子目錄中的標頭檔案;而第二種格式用來包含那些由使用者自己定義的放在當前目錄或其他目錄下的標頭檔案或其它原始檔。

4. 陣列也可以作為函式的實參和形參,若陣列元素作為函式的實參,則其用法與變數相同。當陣列名作為函式的實參和形參時,傳遞的是陣列的地址。當進行按值傳遞的時候,所進行的值傳送是單向的,即只能從實參傳向形參,而不能從形參傳回實參。形參的初值和實參相同,而形參的值發生改變後,實參並不變化,兩者的終值是不同的。而當用陣列名作為函式引數進行傳遞時,由於實際上實參和形參為同一陣列,因此當形引數組發生變化時,實引數組也隨之發生變化。

注:實引數組與形引數組型別應一致,如不一致,結果將出錯;形引數組也可以不指定大小,在定義陣列時陣列名後面跟一個空的方括號,為了在被呼叫函式中處理陣列元素的需要,可以另設一個引數,傳遞陣列元素的個數。如:int sum(int array[],int n);

5. 過載、覆蓋和隱藏的區別?

函式的過載是指C++允許多個同名的函式存在,但同名的各個函式的形參必須有區別:形參的個數不同,或者形參的個數相同,但引數型別有所不同。

覆蓋(Override)是指派生類中存在重新定義的函式,其函式名、引數列、返回值型別必須同父類中的相對應被覆蓋的函式嚴格一致,覆蓋函式和被覆蓋函式只有函式體 (花括號中的部分)不同,當派生類物件呼叫子類中該同名函式時會自動呼叫子類中的覆蓋版本,而不是父類中的被覆蓋函式版本,這種機制就叫做覆蓋。

下面我們從成員函式的角度來講述過載和覆蓋的區別。

成員函式被過載的特徵有: 1)相同的範圍(在同一個類中);2) 函式名字相同;3) 引數不同;4) virtual關鍵字可有可無。
覆蓋的特徵有: 1)不同的範圍(分別位於派生類與基類)2) 函式名字相同;3) 引數相同;4) 基類函式必須有virtual關鍵字。
隱藏是指派生類的函式遮蔽了與其同名的基類函式,規則如下 1)如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無virtual關鍵字,基類的函式將被隱藏(注意別與過載混淆)。2) 如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有virtual關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)。

比如,在下面的程式中:

複製程式碼 #include <iostream.h>class Base
{
public:
void f(int x){ cout <<"Base::f(int) "<< x << endl; }
void f(float x){ cout <<"Base::f(float) "<< x << endl; }
virtualvoid g(void){ cout <<"Base::g(void)"<< endl;}
};

class Derived : public Base
{
public:
virtualvoid g(void){ cout <<"Derived::g(void)"<< endl;}
};

void main(void)
{
Derived d;
Base 
*pb =&d;
pb
->f(42); // 執行結果: Base::f(int) 42pb->f(3.14f); // 執行結果: Base::f(float) 3.14pb->g(); // 執行結果: Derived::g(void)} 複製程式碼

函式Base::f(int)與Base::f(float)相互過載,而Base::g(void)被Derived::g(void)覆蓋。

比如,在下面的程式中:

複製程式碼 #include <iostream.h>class Base
{
public:
virtualvoid f(float x){ cout <<"Base::f(float) "<< x << endl; }
void g(float x){ cout <<"Base::g(float) "<< x << endl; }
void h(float x){ cout <<"Base::h(float) "<< x << endl; }
};

class Derived : public Base
{
public:
virtualvoid f(float x){ cout <<"Derived::f(float) "<< x << endl; }//被繼承之後,virtual 可有可無,但最好有。繼承後,還是虛擬函式。void g(int x){ cout <<"Derived::g(int) "<< x << endl; }
void h(float x){ cout <<"Derived::h(float) "<< x << endl; }
using Base::g;//這句話是用來引用父類中被隱藏的部分的。}; 複製程式碼

通過分析可得:

1) 函式Derived::f(float)覆蓋了Base::f(float)。

2) 函式Derived::g(int)隱藏了Base::g(float),注意,不是過載。

3) 函式Derived::h(float)隱藏了Base::h(float),而不是覆蓋。

看完前面的示例,可能大家還沒明白隱藏與覆蓋到底有什麼區別,因為我們前面都是講的表面現象,怎樣的實現方式,屬於什麼情況。下面我們就要分析覆蓋與隱藏在應用中到底有什麼不同之處。在下面的程式中bp和dp指向同一地址,按理說執行結果應該是相同的,可事實並非如此。

複製程式碼 void main(void)
{
Derived d;
Base 
*pb =&d;
Derived 
*pd =&d;
// Good: behavior depends solely on type of the objectpb->f(3.14f); //執行結果: Derived::f(float) 3.14pd->f(3.14f); //執行結果: Derived::f(float) 3.14
// Bad : behavior depends on type of the pointerpb->g(3.14f); //執行結果: Base::g(float) 3.14pd->g(3.14f); //執行結果: Derived::g(int) 3
// Bad : behavior depends on type of the pointerpb->h(3.14f); //執行結果: Base::h(float) 3.14pd->h(3.14f); //執行結果: Derived::h(float) 3.14} 複製程式碼

請大家注意,f()函式屬於覆蓋,而g()與h()屬於隱藏。從上面的執行結果,我們可以注意到在覆蓋中,用基類指標和派生類指標呼叫函式f() 時,系統都是執行的派生類函式f(),而非基類的f(),這樣實際上就是完成的“介面”功能。而在隱藏方式中,用基類指標和派生類指標呼叫函式f()時,系統會進行區分,基類指標呼叫時,系統執行基類的f(),而派生類指標呼叫時,系統“隱藏”了基類的f(),執行派生類的f(),這也就是“隱藏”的由來。

過載(overload):這個好理解,在同個space域同名的。引數必須不同,有關virtual無關.

覆蓋(override):同名字,同參數,有virtual,覆蓋好理解比如show()函式,A派生了B,如果B中的show()覆蓋了A中的show(),但B中仍然有兩個show(),而不管是A類指標也好,B類物件呼叫也好,都只能呼叫B類自己的那個show();而從A類繼承過來的show()函式真的就被覆蓋了,沒有了嗎? 答案是不對的.這時可以在B類物件顯示的呼叫A類繼承過來的show();

程式程式碼:

複製程式碼 #include <iostream>usingnamespace std; 

class A 

public
virtualvoid show() 

cout 
<< a << endl; 


int a; 
}; 

class B:public A 

public
void show() 

A::show(); 
//顯式地呼叫自己類中的 "由A類繼承過來的show()函式" ,像這種直接顯式指出某個類的某個函式時, 編譯器處理方式是這樣的: 首先在自己類中找有沒有A::show(),如果找到,呼叫.不在繼續在A類中找,如果找不到,則在顯式指出的那個類中(即A類)呼叫那個函式. 這裡當然是在B類中可以找到A::show() ,因為基類中指出了這個函式是virtual函式. 
int b; 
};

int main() 

A a; 
a.a 
=3
a.show(); 

B b; 
b.b 
=4
b.show(); 
b.A::show(); 
//顯示的呼叫自己類中的 "由A類繼承過來的show()函式"return0
}
複製程式碼

總結:

通俗的講B類還是有兩個show(),只是呼叫由A繼承過來的show()只能通過顯式的呼叫方法 [類名::virtual函式名] 而不管是基類A的指標 (B b; A *p = &b; p->show())或者派生類的物件(B b; b.show()),都只能呼叫B類的自己本身存在的show()函式

隱藏hide:

1:同名同參無virtual

2:同名不同參不管有無virtual

程式程式碼:

複製程式碼 class A 

public
void show() {}; //編號1 void rose(int a) {}  //編號2 }; 

class B:public A 

public
void show() {}; //編號3 void rose(int a, int b) {}; //編號4 }; 複製程式碼

類B中的show()和rose()明顯是隱藏了類A的show()和rose() 隱藏的理解: B類中其實有兩個show(),兩個rose(); 但為什麼不叫過載呢?你會這樣想,但我可以告訴你,因為類B中的兩個show(),兩個rose(),不是都可以被B類的物件呼叫的.

編號1和編號2,在類B中哪怕存在,但只能通過類A的指標呼叫,而不能通過B類物件呼叫,如:

程式程式碼:

*=new B; 
p
->show(); 
p
->rose(3); 
p
->rose(3,5); //error

編號3和程式設計4,只能通過類B物件呼叫,而不能通過類A的指標呼叫,如:

程式程式碼:

B b; 
b.show(); 
b.rose(
3,5); 
b.rose(
4); //error

6. 用引數列表可以區分過載函式,為什麼返回值卻不能區分過載函式?

比如說有兩個函式:int fun(); double fun(); 如果寫fun();那麼編譯器就不知道該呼叫誰了。函式呼叫結束後才能確定返回值,但程式在呼叫函式時就需要明確知道呼叫哪一個過載的函式,這前後矛盾。

7. 派生類與基類之間的關係

  • 派生類物件可以使用基類的方法,條件是方法在基類中沒有被宣告為私有的。基類指標可以在不進行顯式型別轉換的情況下指向派生類物件,基類引用可以在不進行顯式型別轉換的情況下引用派生類物件。不過,基類指標或引用只能用於呼叫基類方法,不能呼叫派生類的方法。通常C++要求引用和指標型別與賦給的型別匹配,但這一規則對繼承來說是個例外。不過這種例外只是單向的,不可以將基類物件和地址賦給派生類引用和指標。

8. 靜態聯編和動態聯編

程式呼叫函式時,將使用哪個可執行程式碼塊呢?編譯器負責回答這個問題。將原始碼中的函式呼叫解釋為執行特定的函式程式碼塊被稱為函式名聯編。在C語言中,這非常簡單,因為每個函式名都對應一個不同的函式。在C++中,由於函式過載的緣故,這項任務更復雜。編譯器必須檢視函式引數以及函式名才能確定使用哪個函式。然而,C/C++編譯器可以在編譯過程完成這種聯編。在編譯過程中進行聯編被稱為靜態聯編/繫結,又稱為早期聯編/繫結。不過,虛擬函式使這項工作變得更困難。因為使用哪個函式是不能在編譯時確定的,因為編譯器不知道使用者將選擇哪種型別的物件。所以,編譯器必須生成能夠在程式執行時選擇正確的虛方法的程式碼,這被稱為動態聯編/繫結,又被稱為晚期聯編/繫結

相關推薦

詳解C++派生的轉換以及虛

原文來源:https://www.jb51.net/article/72586.htm# C++基類與派生類的轉換 在公用繼承、私有繼承和保護繼承中,只有公用繼承能較好地保留基類的特徵,它保留了除建構函式和解構函式以外的基類所有成員,基類的公用或保護成員的訪問許可權在派生類中全部都按原樣保留下來

C++派生

派生類的繼承方式總結: 繼承方式 說明 public 基類的public和protected的成員被派生類繼承後,保持原來的狀態 private 基類的public和protected的成員被派生類繼承後,變成派生類的private成員 protected

C++-繼承:派生的關係

成員函式的重定義和名字隱藏 基類的資料成員和成員函式在派生類中都有一份拷貝,派生類能夠直接訪問從基類繼承而來的public和protected成員,且只能夠通過這兩類成員訪問從基類繼承而來的private成員。 派生類不僅可以新增基類沒有的新成員,而且可以對

派生的指針和成員函數調用原理

而且 font 重新定義 -s 繼承 轉型 center span enter 基類與派生類的指針和成員函數調用原理 1.如果以一個基礎類指針指向一個衍生類對象(派生類對象),那麽經由該指針只能訪問基礎類定義的函數(靜態聯翩) 2.如果以一個衍生類指針指向一個基礎類對象,必

派生,父指針指向子對象

namespace 簡單工廠模式 為什麽 對象創建 簡單工廠 pos 釋放 自己的 分享 先看一段代碼: 1 #include<iostream> 2 3 using namespace std; 4 5 class Base{ 6 publi

關於派生相關,以及繼承

多型就是一個基類可以指向其任意派生類的能力,即基類的指標或者引用可以指其派生類,作用就就是希望使用派生具體的實現介面功能 1.多型性只存在類的繼承層次中 2.必須使用基類的指標或者引用才有效. 3.多型可以動態呼叫的函式必須是由virtual宣告的函式 4.多型性是執行時的動態載入,不是連結時也不是

C++模板派生訪問成員必須顯式呼叫(作用域限定)

一個常見的派生類訪問基類成員變數的例子:template<int dim>class A{public:A():b(2){}int b;};template<int dim>class B:public A<dim>{public:   

第三十一節 C++ 繼承之如何在派生呼叫被覆蓋的函式成員(方法)

#include <iostream> using namespace std; /*有些基類的方法在派生類被覆蓋,但有時我們需要呼叫基類的方法。 * 這裡提供了兩種方式去呼叫基類被覆蓋的

C++派生的構造函數和析構函數的調用

str 生命 ons stream all 兩種 col 生命期 析構函數 C++基類和派生類的構造函數和析構函數的調用 1.調用順序   當創建一個派生類的對象時,系統首先自動創建一個基類對象,也就是說,在調用派生類構造函數創建派生類對象之前,系統首先調用基類的構造函數創

為什麽 c++函數模板和模板的 聲明定義需要放到一起?

color code 我們 ack 二進制 通過 如果 之前 類型 將模板的聲明與定義寫在一起實在很不優雅。嘗試用“傳統”方法,及在.h文件裏聲明,在.cpp文件裏定義, 然後在main函數裏包含.h頭文件,這樣會報鏈接錯誤。why!!!!!!!!!!!!! 這是因為函數模

C++虛析構函數的作用及其原理分析

art 收回 顯示 就是 靜態綁定 運行 style 轉載 調用父類 虛析構函數的理論前提是 執行完子類的析構函數,那麽父類的虛構函數必然會被執行。 那麽當用delete釋放一個父類指針所實例化的子類對象時,如果沒有定義虛析構函數,那麽將只會調用父類的析構函數,而不會調用子

C++的解構函式為什麼要用virtual虛解構函式【轉】

(轉自:https://blog.csdn.net/iicy266/article/details/11906457) 知識背景          要弄明白這個問題,首先要了解下C++中的動態繫結。&n

C++Primer_Chap15_面向物件程式設計_List02_定義派生_筆記

class Quote{ public: Quote() = default; Quote(const std::string &book, double sales_price): bookNo(book), price(sales_price){} std::str

cocos2d-x lua 面向物件 ===> 呼叫派生已經被過載了的函式

基類: local Base = class("Base", function () return cc.Layer:create() end) function Base:ctor( ... ) end function Base:test(self, ...

C++結構體的區別(structclass的區別)

C++中的struct對C中的struct進行了擴充,它已經不再只是一個包含不同資料型別的資料結構了,它已經獲取了太多的功能。struct能包含成員函式嗎? 能! struct能繼承嗎? 能!! struct能實現多型嗎? 能!!!  既然這些它都能實現,那它和clas

C++的解構函式為什麼要用virtual虛解構函式

知識背景          要弄明白這個問題,首先要了解下C++中的動態繫結。  正題          直接的講,C++中基類採用virtual虛解構函式是為了防止記憶體洩漏。具體地說,如果派生類中申請了記憶體空間,並在其解構函式中對這些記憶體空間進行釋放

C#派生_161103

C#不支援私有繼承 派生類應當那個被看做是基類所具有的特性和功能的繼承和擴充套件,而不是簡單的派生類大於基類派生類不能選擇性的繼承基類的方法和屬性,必須繼承基類的所有特性和方法派生類可以在繼承基類的基

C#結構體的區分

在類的使用過程中,既不需要很多方法,也不需要從類中繼承,為了提升效能,可以使用結構體來替代類。 與類不同的是,結構體用struct 修飾;結構體中的例項欄位不能自定義預設值,而類可以,編譯器會隱式的給出結構體和類的預設建構函式,但是結構體的預設構造不能顯示給出,不然編譯器會

C++結構體的區別

學習了C++的面向物件,最常見的和寫的就是類結構體,下面主要介紹一下結構體和類的區別。 首先類是C++中面向物件獨有的,但是C和C++中都有結構體,下面我們來看一下C和C++中結構體的區別。這裡主要從封裝、多型、繼承、封裝和訪問許可權幾個方面來說。1、C和C++中結構體的區

談談的this指標(C++)

引入   定義一個類的物件,首先系統已經給這個物件分配了空間,然後會呼叫建構函式(說明:假設存在建構函式)。一個類有多個物件,當程式中呼叫物件的某個函式時,有可能要訪問到這個物件的成員變數。而對於同一個類的每一個物件,都是共享同一份類函式。物件有單獨的變數,但是沒有單獨的函