1. 程式人生 > 實用技巧 >【C++】類-派生和繼承

【C++】類-派生和繼承

類-派生和繼承

目錄

1.基本概念

  • 繼承:保持已有類的特性而構造新類的過程
  • 派生:在已有類的基礎上新增自己的特性而產生新類的過程

2. 語法

單繼承(一個父類)

/*
class 派生類名: 繼承方式 基類名
{
成員宣告;
} 
*/
// eg:
class Derived: public Base
{
public:
Derived ();
~Derived ();
};

多繼承(有多個父類)

/*
class 派生類名:繼承方式1 基類名1,繼承方式2 基類名2,...
{
成員宣告;
}
*/
//eg:
class Derived: public Base1, private Base2
{
public:
Derived ();
~Derived ();
};

派生類的構成:

  • 預設情況下繼承除建構函式和解構函式以外的所有成員
  • 派生類聲明瞭一個和某基類成員同名的新成員,派生的新成員就隱藏或覆蓋了外層同名成員
  • 派生類增加新成員使派生類在功能上有所發展

3. 繼承方式

不同繼承方式的差別體現在:

  • 派生類對基類成員的訪問許可權
  • 通過派生類的物件對基類成員的訪問許可權

許可權的優先順序:public < protected < private。繼承之後父類的成員在子類中的許可權不低於繼承方式的許可權,即:

public不改變許可權

protected:public——>protected

private:public, protected——>private

父類的private一直不能被子類訪問

4. 型別轉換

  • 公有派生類物件可以被當作基類的物件使用,反之則不可。
    • 派生類的物件可以隱含轉換為基類物件;
    • 派生類的物件可以初始化基類的引用;
    • 派生類的指標可以隱含轉換為基類的指標。
  • 通過基類物件名、指標只能使用從基類繼承的成員。
#include <iostream>
using namespace std; class Base1 { //基類Base1定義
public:
	void display() const {
		cout << "Base1::display()" << endl;
	}
};
class Base2 : public Base1 { //公有派生類Base2定義
public:
	void display() const {
		cout << "Base2::display()" << endl;
	}
};
class Derived : public Base2 { //公有派生類Derived定義
public:
	void display() const {
		cout << "Derived::display()" << endl;
	}
};
void fun(Base1* ptr) { //引數為指向基類物件的指標
	ptr->display(); //"物件指標->成員名"
}
int main() { //主函式
	Base1 base1; //宣告Base1類物件
	Base2 base2; //宣告Base2類物件
	Derived derived; //宣告Derived類物件
	fun(&base1); //用Base1物件的指標呼叫fun函式
	fun(&base2); //用Base2物件的指標呼叫fun函式
	fun(&derived); //用Derived物件的指標呼叫fun函式
	return 0;
}

都只能呼叫基類的display函式,即不要重寫繼承的非虛擬函式

5. 派生類的構造、解構函式

建構函式

  • 預設情況下不繼承基類的建構函式,使用using Base::Base()指定繼承基類建構函式,此時只能用建構函式對基類成員初始化

  • 自行定義建構函式:

    • 派生類新增成員:派生類定義建構函式初始化;
    • 繼承來的成員:自動呼叫基類建構函式進行初始化;
    • 派生類的建構函式需要給基類的建構函式傳遞引數。
  • 語法:多繼承+組合類

  • 派生類名::派生類名(形參表):
    基類名1(引數), 基類名2(引數), ..., 基類名n(引數),
    本類成員(含物件成員)初始化列表
    {
    //其他初始化
    };
    
  • 建構函式的執行順序

  1. 呼叫基類建構函式。

    順序按照它們被繼承時宣告的順序(從左向右)。

  2. 對初始化列表中的成員進行初始化。

    順序按照它們在類中定義的順序。

    物件成員初化時自動呼叫其所屬類的建構函式。由初始化列表提供引數。

  3. 執行派生類的建構函式體中的內容。

複製建構函式

  • 一般都要為基類的複製建構函式傳遞引數。
  • 複製建構函式只能接受一個引數,既用來初始化派生類定義的成員,也將被傳遞給基類的複製建構函式。
  • 基類的複製建構函式形參型別是基類物件的引用,實參可以是派生類物件的引用

解構函式

  • 解構函式不被繼承,派生類如果需要,要自行宣告解構函式。
  • 宣告方法與無繼承關係時類的解構函式相同。
  • 不需要顯式地呼叫基類的解構函式,系統會自動隱式呼叫。
  • 先執行派生類解構函式的函式體,再呼叫基類的解構函式。
#include<iostream>
using namespace std;

class Base1 {
public:
	Base1(int val) {
		x = val;
		cout << "Calling constructor of base1..." << endl;
	}
	Base1(Base1& a) {
		x = a.x;
		cout << "Calling copy constructor of base1..." << endl;
	}
	~Base1() {
		cout << "Calling destructor of base1..." << endl;
	}
private:
	int x;
};

class Base2 {
public:
	Base2(int val) {
		y = val;
		cout << "Calling constructor of base2..." << endl;
	}
	Base2(Base2& b) {
		y = b.y;
		cout << "Calling copy constructor of base2..." << endl;
	}
	~Base2() {
		cout << "Calling destructor of base2..." << endl;
	}
private:
	int y;
};

class Derived:
	public Base1, public Base2 {
public:
	Derived(int val1, int val2, int val3) :
		Base1(val1), Base2(val2) {
		cout << "Calling constructor of derived..." << endl;
		z = val3;
	}
	Derived(Derived& c) :
		Base1(c), Base2(c) {
		cout << "Calling copy constructor of derived..." << endl;
		z = c.z;
	}
	~Derived() {
		cout << "Calling destructor of derived..." << endl;
	}
private:
	int z;
};

int main() {
	Derived d(1, 2, 2);
	cout << "***********************" << endl;
	Derived newd(d);
	cout << "***********************" << endl;
	return 0;
}

輸出結果:

Calling constructor of base1...
Calling constructor of base2...
Calling constructor of derived...


Calling copy constructor of base1...
Calling copy constructor of base2...
Calling copy constructor of derived...


Calling destructor of derived...
Calling destructor of base2...
Calling destructor of base1...
Calling destructor of derived...
Calling destructor of base2...
Calling destructor of base1...