c++中多重繼承與虛繼承的實現(xiàn)
一、多重繼承(Multiple Inheritance)
1. 基本概念
多重繼承是指一個派生類同時繼承多個基類,允許派生類復用多個基類的屬性和方法,是C++區(qū)別于Java等語言的重要特性之一。
語法格式:
class 派生類名 : 繼承方式1 基類1, 繼承方式2 基類2, ... {
// 派生類成員
};
- 繼承方式:
public/protected/private(和單繼承規(guī)則一致); - 核心價值:讓一個類同時擁有多個類的特性(比如“汽車”同時繼承“交通工具”和“機械產(chǎn)品”)。
2. 簡單示例(無沖突的多重繼承)
#include <iostream>
using namespace std;
// 基類1:飛行動物
class Flyable {
public:
void fly() {
cout << "能飛" << endl;
}
};
// 基類2:游泳動物
class Swimmable {
public:
void swim() {
cout << "能游泳" << endl;
}
};
// 派生類:鴨子(同時繼承飛行動物和游泳動物)
class Duck : public Flyable, public Swimmable {
public:
void quack() {
cout << "嘎嘎叫" << endl;
}
};
int main() {
Duck duck;
duck.fly(); // 繼承自Flyable
duck.swim(); // 繼承自Swimmable
duck.quack(); // 自身方法
return 0;
}
輸出:
能飛
能游泳
嘎嘎叫
這個示例中,Duck同時復用了Flyable和Swimmable的方法,無任何沖突,是多重繼承的理想場景。
3. 多重繼承的核心問題:菱形繼承(鉆石繼承)
當多個基類繼承自同一個“共同基類”,而派生類又同時繼承這些基類時,會形成菱形結構,導致兩個核心問題:
- 數(shù)據(jù)冗余:共同基類的成員會在派生類中存在多份副本;
- 二義性:訪問共同基類的成員時,編譯器無法確定訪問哪一份副本。
菱形繼承示例(有問題):
#include <iostream>
using namespace std;
// 共同基類:動物
class Animal {
public:
int age; // 共同成員
Animal(int a) : age(a) {}
};
// 基類1:飛行動物(繼承Animal)
class Flyable : public Animal {
public:
Flyable(int a) : Animal(a) {}
void fly() { cout << "能飛,年齡:" << age << endl; }
};
// 基類2:游泳動物(繼承Animal)
class Swimmable : public Animal {
public:
Swimmable(int a) : Animal(a) {}
void swim() { cout << "能游泳,年齡:" << age << endl; }
};
// 派生類:鴨子(同時繼承Flyable和Swimmable)
class Duck : public Flyable, public Swimmable {
public:
// 問題1:構造函數(shù)需要初始化兩次Animal(數(shù)據(jù)冗余)
Duck(int a) : Flyable(a), Swimmable(a) {}
void quack() { cout << "嘎嘎叫" << endl; }
};
int main() {
Duck duck(2);
// 問題2:訪問age會觸發(fā)二義性編譯錯誤
// cout << duck.age << endl; // 報錯:ambiguous reference to 'age'
// 強行指定作用域可以訪問,但本質是兩份age(數(shù)據(jù)冗余)
cout << duck.Flyable::age << endl; // 2
cout << duck.Swimmable::age << endl; // 2
return 0;
}
關鍵問題分析:
- Duck對象中存在兩份age(分別來自Flyable和Swimmable),即使初始化值相同,也是獨立的內(nèi)存空間;
- 直接訪問duck.age會編譯報錯,因為編譯器不知道你要訪問哪一份age;
- 這種冗余不僅浪費內(nèi)存,還可能導致邏輯錯誤(比如修改其中一份,另一份不變)。
二、虛繼承(Virtual Inheritance)
1. 基本概念
虛繼承是C++為解決菱形繼承的二義性和數(shù)據(jù)冗余設計的機制:通過virtual關鍵字聲明繼承,讓“共同基類”成為虛基類,此時無論派生多少次,共同基類在最終派生類中只保留一份實例。
語法格式:
class 派生類名 : virtual 繼承方式 基類名 {
// 成員
};
virtual關鍵字加在“繼承方式”前;- 核心作用:讓共同基類的實例在最終派生類中唯一。
2. 虛繼承解決菱形繼承的示例
修改上面的代碼,給Flyable和Swimmable的繼承加virtual:
#include <iostream>
using namespace std;
// 共同基類:動物
class Animal {
public:
int age;
Animal(int a) : age(a) {
cout << "Animal構造:age=" << a << endl;
}
};
// 虛繼承Animal:飛行動物
class Flyable : virtual public Animal {
public:
// 虛繼承下,這里的構造函數(shù)不會直接初始化Animal(由最終派生類統(tǒng)一初始化)
Flyable(int a) : Animal(a) {}
void fly() { cout << "能飛,年齡:" << age << endl; }
};
// 虛繼承Animal:游泳動物
class Swimmable : virtual public Animal {
public:
Swimmable(int a) : Animal(a) {}
void swim() { cout << "能游泳,年齡:" << age << endl; }
};
// 最終派生類:鴨子
class Duck : public Flyable, public Swimmable {
public:
// 虛繼承下,最終派生類必須直接初始化虛基類Animal(唯一入口)
Duck(int a) : Animal(a), Flyable(a), Swimmable(a) {}
void quack() { cout << "嘎嘎叫" << endl; }
};
int main() {
Duck duck(2);
// 無二義性:只有一份age
cout << duck.age << endl; // 輸出2
duck.fly(); // 能飛,年齡:2
duck.swim(); // 能游泳,年齡:2
return 0;
}輸出:
Animal構造:age=2
2
能飛,年齡:2
能游泳,年齡:2
關鍵變化:
- 數(shù)據(jù)冗余解決:Duck對象中只有一份age,不再有重復副本;
- 二義性解決:直接訪問duck.age無編譯錯誤,編譯器明確知道訪問唯一的age;
- 構造規(guī)則變化:虛基類的構造函數(shù)由最終派生類統(tǒng)一初始化(即使中間基類寫了初始化,也會被忽略),保證虛基類只構造一次。
3. 虛繼承的底層原理
虛繼承的實現(xiàn)依賴編譯器的虛基類表(vbtable):
- 每個虛繼承的類會存儲一個指向“虛基類表”的指針;
- 虛基類表記錄了當前類到虛基類實例的偏移量;
- 通過這個表,所有派生類都能找到唯一的虛基類實例,避免冗余和二義性。
提示:虛繼承有輕微的性能開銷(指針訪問+表查詢),因此僅在需要解決菱形繼承時使用,不要濫用。
三、總結
- 多重繼承:允許一個類繼承多個基類,復用多類特性,但會引發(fā)菱形繼承的數(shù)據(jù)冗余和二義性問題;
- 虛繼承:通過
virtual關鍵字聲明繼承,將共同基類變?yōu)樘摶?,使其在最終派生類中僅保留一份實例,解決菱形繼承的核心問題; - 關鍵規(guī)則:虛基類的構造函數(shù)由最終派生類統(tǒng)一初始化,中間基類對虛基類的初始化會被編譯器忽略。
補充:多重繼承的其他注意事項
- 如果多個基類有同名方法,即使不是菱形繼承,訪問時也需要指定作用域(比如
duck.Flyable::func()); - 實際開發(fā)中,應盡量避免復雜的多重繼承(可通過組合/接口替代),僅在確有必要時使用;
- C++11后的
std::enable_shared_from_this、Qt的QObject等經(jīng)典場景會用到虛繼承。
到此這篇關于c++中多重繼承與虛繼承的實現(xiàn)的文章就介紹到這了,更多相關c++ 多重繼承與虛繼承內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Opencv 馬賽克和毛玻璃效果與圖片融合的實現(xiàn)
這篇文章主要為大家詳細介紹了通過OpenCV實現(xiàn)馬賽克和毛玻璃濾鏡效果與圖片的融合,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11

