1. 程式人生 > >C++程式設計學習筆記 複習/拾遺 8

C++程式設計學習筆記 複習/拾遺 8

繼承的應用

#include <iostream>
using namespace std;
class A
{
    public:
        void f(int i)
        {  cout<<i<<endl;  }
        void g()
        {  cout<<"A\n";  }
};

class B:private A 
{
  public:
    void h()
    {  cout<<"B\n";  }
     A::f; 
     //A::f; 該語句的含義是將類A 中的公有成員f() 
從私有繼承方式的派生類B 中申明為公有的
};

程式中A::f; 的含義是將類A 中的公有成員f() 從私有繼承方式的派生類B 中申明為公有的,B 的派生類物件可以訪問成員f()。因此在main()中,語句b.f(10); 是正確的。該語句稱為訪問申明,它是私有繼承方式中的一種呼叫機制,即在私有繼承方式下,用於恢復名字的訪問許可權。

int main()
{
    B b;
    b.f(10);//true
    b.g();    //error
    b.h();//true
    return 0;
}
語句b.g(); 是錯誤訪問,有編譯錯。若註釋該語句,執行結果為:10
                       B
將class B:private A 改為class B:public A,則無編譯錯,原程式輸出為:10
                               A
                               B

Dog類(派生類)從Ammal類(基類)派生的過程:

  • 吸收基類成員:繼承後,派生類繼承基類的除了建構函式和解構函式之外的成員。本例中Dog類繼承了基類的Mammal類中的GetAge()、GetWeight()、SetAge()、SetWeight()、Speak()、itsAge、itsWeight;
  • 改造基類成員:當派生類的同名屬性和行為具有和基類不同的特徵時,就要在派生類中重新宣告或者定義,賦予新的含義,從而完成對基類成員的改造。這樣就隱藏了基類中的同名成員(同名重寫/覆蓋規則)。本例中Dog類與Speak()函式進行改造,因為並不是所有動物都有共同語言。
  • 新增新成員:派生類中新增新成員使得派生類在功能上有所擴充套件。本例中Dog類添加了新的類成員itsColor、GetColor()、SetColor( ),從而實現了派生類Dog在功能上的擴充套件。

基於專案的多檔案管理

//Ammal.h
#include <iostream>
using namespace std;
enum MyColor{BLACK, WHITE};
class Mammal
{
public:
	Mammal();
	 ~ Mammal();
	Mammal(int age, int weight):itsAge(age),itsWeight(weight){}
	int GetAge(){return itsAge;}
	int GetWeight(){return itsWeight;}
	void SetAge(int age){itsAge = age;}
	void SetWeight(int weight){itsWeight = weight;}
	void Speak(){cout<<"Mammal language!"<<endl;}
protected:
	int itsAge;     //年齡   
	int itsWeight;  //體重
};

//Ammal.cpp檔案中
#include "Ammal.h"
Mammal::Mammal()
{

}
Mammal::~Mammal()
{

}

//Dog.h
#include "Ammal.h"
class Dog : public Mammal
//公有繼承方式
{
public:
	Dog();
      ~Dog();
	MyColor GetColor(){return itsColor;}
	void SetColor(MyColor color){itsColor = color;}
	void Speak(){cout<<"Dog language!"<<endl;}
protected:
	MyColor itsColor;
};

//Dog.cpp
#include "Dog.h"
Dog::Dog()
{

}
Dog::~Dog()
{

}

//main.cpp
#include "Dog.h"
int main()
{
	Dog dog;
	dog.SetAge(25);
	dog.SetWeight(50);
	dog.SetColor(WHITE);
	cout<<"Ddog age = "<<dog.GetAge()<<endl;
	cout<<"Dog weight = "<<dog.GetWeight()<<endl;	
	cout<<"Dog color = "<<dog.GetColor()<<endl;	
	dog.Speak();
	return 0;
};

執行結果:
Dog age = 25
Dog weight = 50
Dog color = 1
Dog language!

管理步驟

  1. 建立一個控制檯型別的專案L9.3,帶一個main.cpp檔案,其內容是main函式
  2. 在L9.3專案中為Mammal類建立2個檔案(Ammal.h和Ammal.cpp檔案),為Dog類建立2個檔案(Dog.h和Dog.cpp檔案)

在專案名L9.3,右鍵單擊選擇新建檔案,新增新檔案,如圖:在這裡插入圖片描述
檔案->新建->新建一個類,如圖:
系統自動加了框架程式碼
在這裡插入圖片描述

  1. 輸入基類名Mammal
  2. 勾選建構函式與解構函式
  3. 預設檔名Mammal.cpp及
    Mammal.h
    在這裡插入圖片描述
  1. 輸入子類名
  2. 勾選從其它類繼承
  3. 選擇繼承方式
  4. 輸入父類名
    在這裡插入圖片描述

賦值相容規則(向上轉型)

一個公有派生類的物件在使用上可以被當作基類的物件,反之則禁止。具體表現在:

  • 派生類的物件可以被賦值給基類物件。
  • 派生類的物件可以初始化基類的引用。
  • 指向基類的指標也可以指向派生類,即一個公有派生類物件的指標值可以賦值給(或初始化)一個基類指標。
  • 利用這樣的指標或引用,只能訪問派生類物件中從基類繼承過來的成員,無法訪問派生類的自有成員。
    在這裡插入圖片描述注意:派生類的拷貝建構函式如果要重新定義,其引數只有一個,即派生類物件引用,根據賦值相容規則,派生類物件可以用來初始化基類的拷貝建構函式的基類物件引用。

在C++中,這與Java不同,Java系統會自動呼叫子類中的方法。因為Java中預設是虛擬函式,動態繫結機制能識別出物件轉型前的型別。而C++中虛擬函式必須加virtual。所以Java中沒有賦值相容規則。

例9.4:賦值相容示例,要求用類的公有繼承方法輸出圓資訊,包括半徑和圓心。

#include <iostream>
using namespace std;
class Point
{protected:
	double x,y;	
public:
	Point(double a=0,double b=0)
	{x=a;y=b;	}
	void Show()
	{cout<<x<<","<<y<<endl;	}
};
class Circle:public Point
{	
public:	
    Circle(double a=0,double b=0,double c=0):Point(a,b)  
    {  r=c;  } 
    void Show();
private:
    double r;
};
void Circle::Show()
{	cout<<"半徑="<<r<<endl<<"圓心=";
	cout<<x<<","<<y<<endl;//等價於p.Show();	
}
int main()
{
Circle c1(100,100,10);//定義派生類物件 
Point p1=c1,*p2=&c1,&p3=c1;//基類物件、物件指標和物件引用 
p1.Show();
p2->Show();//賦值相容規則
p3.Show();	
return 0;
}

執行結果:100,100
100,100
100,100

組合與繼承的比較

  • “包含 has-a”關係用組合來表達
  • “屬於is-a”關係用繼承來表達
  • 在更多的時候,組合關係比繼承更能使系統具有高度的靈活性,可維護行,並且提高系統的可重用性。

許多時候都要求將組合與繼承兩種技術結合起來使用,建立一個更復雜的類。

圓類設計與分析-平臺9.9題

設有一個Point類,有資料成員x和y。另有一個Color類,有資料成員a。
要求從Point類公有派生出Circle類,增加了資料成員r和顏色物件p,這3個類都定義了Show函式輸出其資料資訊。要求Circle類的定義採用組合與繼承技術結合。

#include <iostream>
using namespace std;
enum MyColor{BLACK, WHITE,RED,YELLOW,GREEN};
class Point
{protected:
	double x,y;	
public:
	Point(double a,double b)
	{x=a;y=b;
	cout<<"呼叫Point類帶參建構函式"<<endl;
	}
	Point()
	{x=0;y=0;
	cout<<"呼叫Point類無參建構函式"<<endl;
	}
	void Show()
	{	cout<<x<<","<<y;	}	
};
class Color
{
protected:
		MyColor a;
public:	
	Color(MyColor b) 
	{a=b;
	 cout<<"呼叫Color類帶參建構函式"<<endl; 
	}
	Color() 
	{a=BLACK;
	 cout<<"呼叫Color類無參建構函式"<<endl; 
	}
	Color(Color &r) 
	{a=r.a;
	 cout<<"呼叫Color類拷貝建構函式"<<endl; 
	}
	void Show()
	{
		cout<<"顏色=";
		switch (a)
		{
			case 0:cout<<"BLACK"<<endl;break;
			case 1:cout<<"WHITE"<<endl;break;
			case 2:cout<<"RED"<<endl;break;
			case 3:cout<<"YELLOW"<<endl;break;
			case 4:cout<<"GREEN"<<endl;break;
			default:cout<<"QITA"<<endl;
		}
	}	
};
class Circle:public Point
{	
public:	
    Circle(double a,double b,double c,Color &d):Point(a,b),p(d)  
    {  r=c;  }     	
	void Show();
private:
	double r;
	Color p;	
};
void Circle::Show()
{
	cout<<"半徑="<<r<<endl<<"圓心=("<<x<<","<<y<<")"<<endl;
	p.Show();		
}

int main()
{
	Color b(RED);
	Circle c1(100,100,10,b);//定義圓物件
	c1.Show();//呼叫成員函式		
	return 0;
}

//組合和繼承結合時,先呼叫基類建構函式,再呼叫組合物件的建構函式,最後呼叫派生類的建構函式。

執行結果:
呼叫Color類帶參建構函式
呼叫Point類帶參建構函式
呼叫Color類拷貝建構函式
半徑=10
圓心=(100,100)
顏色=RED

基類的成員函式在派生類中過載

例9.6 基類的成員函式在派生類中過載示例1
#include <iostream>
using namespace std;
class A  
{  int a;
public:  
    void fn()  
    {a=0;
    cout<<"A::"<<a<<endl;
    }   
    void fn(int a)  
    {this->a=a;
    cout<<"A::"<<a<<endl;
    }  
};    
class B : public A  
{  
  int b;   
};  
 int main()  
{  B b;  //B類沒有對fn函式進行同名覆蓋
    b.fn(3);
    b.fn();
    return 0;  
}

結果:
A::3
A::0
例9.7:基類的成員函式在派生類中過載示例2-在例9.6基礎上改

#include <iostream>
using namespace std;
class A  
{  int a;
public:  
    void fn()  
    {cout<<"A::0"<<endl;}    
    void fn(int a)  
    {this->a=a;
    cout<<"A::"<<a<<endl;}  
};    
class B : public A  
{  
public:  
    void fn(int a)  //B類對fn函式進行同名覆蓋
    {cout<<"B::"<<a<<endl;}  
};    
int main()  
{   B b;  
    b.fn(3);
    b.fn();
    return 0;  
}


結果:
錯誤資訊:no matching function for call to 'B::fn()'
例9.8:基類的成員函式在派生類中過載示例2修改

#include <iostream>
using namespace std;
class A  
{  int a;
public:  
    void fn()  
    {cout<<"A::0"<<endl;}    
    void fn(int a)  
    {this->a=a;
    cout<<"A::"<<a<<endl;}  
};    
class B : public A  
{  
public:  
    void fn(int a)  //同名覆蓋 
    {cout<<"B::"<<a<<endl;}  
    void fn()  //同名覆蓋 
    {A::fn();//用類名::,將隱藏的A類函式呼叫 
    }  
};    
int main()  
{   B b;  
    b.fn(3);//同名覆蓋,呼叫B類重寫的函式 
    b.A::fn(3);//用類名::,將隱藏的A類函式呼叫 
    b.fn();//同名覆蓋,間接呼叫隱藏的A類函式 
    return 0;  
}

當派生類寫一個和基類同名(無論引數列表相同或不相同)的函式時,此時發生的動作叫“覆蓋”。覆蓋的意思,就是基類的同名函式,在派生類內,將變得無法直接呼叫(但可以間接呼叫)。

要訪問A的過載函式,一種方式是直接用基類名::函式,可以將隱藏的基類函式呼叫,比如:b.A::fn(3); 還有一種方法是間接呼叫,在派生類中再寫一個同名函式,裡面程式碼用基類名::函式間接呼叫,比如:b.fn();。

相關推薦

C++程式設計學習筆記 複習/拾遺 8

繼承的應用 #include <iostream> using namespace std; class A { public: void f(int i) { cout<<i<<en

C++程式設計學習筆記 複習/拾遺 7

組合與繼承 程式碼重用是面向物件最引人注目的功能之一: 可以通過建立新類來複用程式碼,而不必再重頭開始編寫。 可以使用別人已經開發並除錯好的類。 類的重用 在新類中使用其他類的物件。即新類由多種類的物件組成,這種方法稱為組合。 在現有類的基礎

C++程式設計學習筆記 複習/拾遺 6

多型性與過載 多型性是面向物件程式設計的重要特徵之一。多型是指一個名字有多種語義,或一個相同介面有多種實現;或是指發出同樣的訊息被不同型別的物件接受而導致完全不同的行為,即物件根據所接收到的訊息做出相應的操作。 訊息主要是指對類的成員函式的呼叫 不同的行為是指不同的實現

C++程式設計學習筆記 複習/拾遺 5

靜態與友元 封裝性:物件的狀態資訊隱藏在物件內部,不允許外部程式直接訪問物件內部資訊,而是通過該類所提供的方法來實現對內部資訊的操作與訪問。 類外如何訪問被封裝的成員(private 和 protected成員)? –通過物件名.公有成員函式

C++程式設計學習筆記 複習/拾遺 4

類和物件應用 基於專案的多檔案管理 將類的設計與類的使用分離 類定義與main函式(類測試) 不在一個檔案中。 將類的宣告和類的成員函式實現分離 類定義與成員函式定義 不在一個檔案中 優點: 便於分工合作 便於軟

C++程式設計學習筆記 複習/拾遺 3

拷貝建構函式與解構函式 字串函式 例4.1:類中資料成員是字串 #include <iostream> #include <cstring>//字串函式宣告所在的標頭檔案 using namespace std; class HelloWorld { pri

C++程式設計學習筆記 複習/拾遺 1

面向物件概述與c++輸入輸出 1 使用<iostream>時,由於C++的標準庫中所有識別符號都被定義於一個名為std的namespace中,因此其後必須帶上語句“using namespace std;”。 2 #include <iostr

C++程式設計學習筆記 複習/拾遺 2

建構函式與物件初始化 建構函式用於建立類物件,初始化其成員。 解構函式用於撤銷類物件。 物件的私有資料成員初始化 若物件定義時若未顯式初始化,與變數類似,全域性物件和靜態物件在定義時初值為0,區域性物件在定義時初值為不確定的值。一旦建立一個物件,物件通常都需要有一個有意義的初

C/C++程式設計學習筆記二:C語言的函式中,如何使用指標交換兩個數的值,深入理解指標

 使用外部函式交換兩個變數的值,一個再簡單不過的事情,但是在C/C++中,正確實現該功能反應了你對指標和引用等重要知識的掌握程度。本文列舉了幾種常見的寫法,其中前三種是錯誤的,後兩種是正確的。第四種使

Linux C程式設計學習筆記(2):open、creat、close函式及檔案的建立、開啟與關閉

my_create.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include

QT之C++和QML混合程式設計學習筆記

QML中使用C++物件 建立一個測試用的C++物件 #ifndef PIECHART_H #define PIECHART_H #include <QtQuick/QQuickPaintedItem> #include <QColor> #include <

C語言學習筆記---C專家程式設計 什麼時候陣列和指標相同

 什麼時候陣列和指標是相同的: 規則1:表示式中的陣列名(與申明不同)被編譯器當作指向該陣列第一個元素的指標 規則2:下標總是與指標的偏移量相同 規則3:在函式引數的宣告中,陣列名被編譯器當作指向該陣列第一個元素的指標 規則1的例外情況: 1、陣列作為sizeo

C語言程式設計 學習筆記 動態記憶體分配(malloc)

如果輸入程式時,先告訴你個數,然後再輸入,要記錄每個資料(類似動態陣列) C99之前應該怎麼做呢? malloc()函式的作用就在此: int *a = (int*)malloc(n*sizeof(int)); malloc()函式的作用是向記憶體申請一個n*

C語言程式設計 學習筆記 連結串列

接可變陣列 但如果我們可以使用BLOCK,將其都拼接在一起,並不是用上面的方法複製貼上。每一個BLOCK會有一個單元指向的是下一個BLOCK的地址,這樣就不會有上述的問題了 所以對於一個單元,它裡面應該分成兩部分: 1.資料 2.下一個單元的地址(指標) 這樣指向的下一個資料結構也應是

C語言程式設計 學習筆記 12.3 多個原始碼檔案、標頭檔案、宣告

我們經常在做“分而治之”的事情(多個.c檔案): 1.main()裡的程式碼太長了適合分成幾個函式 2.一個原始碼檔案太長了適合分成幾個檔案 3.兩個獨立的原始碼檔案不能編譯成可執行的程式 對於(1),我們可以舉以下例子: 有個主函式main.c,有另外一個函式

c語言學習筆記8)位運算子,++,--運算子的用法

摘要:總結了位運算子,++,--運算子的用法,給出了一個使用異或方法巧解面試題的例子,分析了貪心法的規則。 一、位運算子用法     c語言中的位運算子主要有以下幾種:         使用的時候主

C語言程式設計 學習筆記 字串(II)(字串輸入輸出,字串陣列,程式引數)

字串輸入輸出: char str[8]; scanf("%s",&str); printf("%s",str); scanf表示讀入一個單詞(到空格、tab、回車為止) scanf是不安全的,因為這樣不知道要讀入的內容的長度,在一些情況中會出現問題:

C++ set學習筆記

all pri cto 等於 中序 center type 節點 begin Stl~(multi)set set集合容器:實現了紅黑樹的平衡二叉檢索樹的數據結構,插入元素時,它會自動調整二叉樹的排列,把元素放到適當的位置,以保證每個子樹根節點鍵值大於左子樹所有節點的鍵

[C/C++] C++ Primer學習筆記

轉義 寫到 十六進制 程序 結果 否則 筆記 end 情況 下面記錄我每天看書學到的以前不太清楚的概念和用法: Day 1 endl:具有輸出換行的效果,並刷新與設備相關聯的緩沖區。 註:在調試程序過程中插入的輸出語句都應刷新輸出流,否則可能會造成程序崩潰,將會導致程序出錯

C++ Primer 學習筆記_5_變量和基本類型(續2)

key 情況 boa 類和對象 類定義 優點 splay 查看 變量定義  變量和基本類型 七、枚舉 枚舉不但定義了整數常量集,並且還把它們聚集成組。 枚舉與簡單的const常量相比孰優孰劣, 通過以下一段代