一文徹底讀懂C++的繼承機制
一、繼承的概念及定義
1.1 繼承的概念
在沒有接觸繼承之前我們要設計兩個類Student和Teacher,Student和Teacher都有姓名/地址/電話/年齡等成員變量,都有identity身份認證的成員函數(shù),設計到兩個類里面就是冗余的。當然他們也有一些不同的成員變量和函數(shù),比如老師獨有成員變量是職稱,學生的獨有成員變量是學號;學生生獨有的成員函數(shù)是學習,老師獨有的成員函數(shù)是教授。
class Student
{
public:
void Study()
{
//學習
}
void identity()
{
//身份認證
}
private:
string _name; //姓名
string _address; //地址
string _tel; //電話
int _age; //年齡
int _stuid; //學號
};
class Teacher
{
public:
void teach()
{
//教授
}
void identity()
{
//身份認證
}
private:
string _name; //姓名
string _address; //地址
string _tel; //電話
int _age; //年齡
int _title; //職工號
};利用繼承的方法我們可以將兩個類中的公共部分提取出來封裝成單獨一個類Person,再使Teacher/Student分別繼承Person。這樣Teacher/Student中既有自己特有的成員變量(函數(shù))也有公共的成員變量(函數(shù)),大大避免了代碼的冗余。
#include<string.h>
class Person
{
public:
void identity()
{
//身份認證
}
string _name; //姓名
string _address; //地址
string _tel; //電話
int _age; //年齡
};
class Student:public Person //Stuedent繼承Person類
{
public:
void Study()
{
//學習
}
private:
int _stuid; //學號
};
class Teacher:public Person //Teacher繼承Person類
{
public:
void teach()
{
//教授
}
private:
int _title; //職工號
};
繼承機制是面向對象程序設計使代碼可以復用的最重要的手段,它允許我們在保持原有類特性的基礎上進行擴展,增加方法(成員函數(shù))和屬性(成員變量),這樣產(chǎn)生新的類叫做派生類。繼承呈現(xiàn)了面向對象程序設計的層次結構,體現(xiàn)了由簡單到到復雜的認知過程。以前我們接觸的都是函數(shù)層面的復用,繼承是類設計層次的復用。
1.2 繼承的定義
1.2.1 定義格式
下面Person是基類,也稱作父類;Student是派生類,也稱作子類。

1.2.2 繼承基類成員訪問方式的變化
基類private成員在派生類中無論以什么方式繼承都是不可見的。這里的不可見是指基類的私有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管在類里面還是類外面都不能去訪問它。
基類private成員在派生類中不能訪問,如果基類不想在類外直接訪問,但還需要在派生類中可以訪問,就定義成protected。可以看出保護成員限定符是因繼承才出現(xiàn)的。
實際上面的表格我們總結一下就會發(fā)現(xiàn),基類的私有成員在派生類中都是不可見的。基類的其他成員在派生類中的訪問方式==Min(成員在基類的訪問限定符,繼承方式)public>protected>private。

使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯式地寫出繼承方式。
在實際運用中一般使用的都是public繼承,幾乎很少使用protected/private繼承,也不提倡使用protected/private繼承,因為protected/private繼承下來的成員都只能在派生類中使用,實際中擴展維護性不強。
1.3 繼承類模板
在 C++ 中,類模板可以被繼承,這種方式稱為 "繼承類模板"。通過繼承類模板,我們可以創(chuàng)建更具體或更特殊化的類,同時復用模板的通用邏輯。
繼承類模板主要有兩種場景:
- 普通類繼承類模板的實例化版本
- 類模板繼承另一個類模板
1.普通類繼承類模板的實例化版本
當一個普通類繼承自一個已經(jīng)實例化的類模板時,需要指定模板參數(shù):
// 定義一個類模板
template <typename T>
class Container {
protected:
T data;
public:
Container(T val) : data(val) {}
T get() const { return data; }
void set(T val) { data = val; }
};
// 普通類繼承實例化的類模板
class IntContainer : public Container<int> {
public:
IntContainer(int val) : Container<int>(val) {}
// 新增方法
void increment() { data++; }
};2.類模板繼承另一個類模板
類模板可以繼承另一個類模板,此時可以使用自身的模板參數(shù)作為基類模板的參數(shù):
#include <iostream>
// 基類模板
template <typename T>
class Base {
protected:
T value;
public:
Base(T v) : value(v) {}
void print() const {
std::cout << "Base value: " << value << std::endl;
}
};
// 派生類模板繼承基類模板
template <typename T, typename U>
class Derived : public Base<T> {
private:
U extra;
public:
// 注意初始化列表中需要顯式指定基類模板
Derived(T v, U e) : Base<T>(v), extra(e) {}
void show() const {
// 訪問基類成員時,可能需要使用this指針或Base<T>::限定
std::cout << "Derived value: " << this->value
<< ", Extra: " << extra << std::endl;
}
};
int main() {
Derived<int, std::string> obj(42, "example");
obj.print(); // 調用基類方法
obj.show(); // 調用派生類方法
return 0;
}
注意事項:
訪問基類成員:在派生類模板中訪問基類模板的成員時,可能需要使用
this->指針或Base<T>::限定符,幫助編譯器識別成員。模板參數(shù)傳遞:派生類模板可以將自身的模板參數(shù)傳遞給基類模板,也可以使用固定類型:
template <typename T>
class Derived : public Base<double> { // 固定使用double類型
// ...
};二、基類和派生類間的轉化
在 C++ 中,public 繼承的派生類對象與基類之間存在三種常見的賦值 / 關聯(lián)方式:派生類對象賦值給基類指針、派生類對象賦值給基類引用、派生類對象直接賦值給基類對象。這三種方式的本質和效果有顯著區(qū)別,核心在于是否保留派生類的特性以及是否觸發(fā)多態(tài)行為。
1.派生類對象賦值給基類指針(Base* ptr = &derivedObj;)
- 本質:基類指針指向派生類對象的基類部分(但指針可以間接訪問派生類的完整信息,配合虛函數(shù)實現(xiàn)多態(tài))。
- 特性:
- 指針的靜態(tài)類型是
Base*,但動態(tài)類型是Derived*(指向對象的實際類型)。 - 可以通過指針調用基類的成員(包括虛函數(shù)),若調用虛函數(shù),會根據(jù)動態(tài)類型觸發(fā)動態(tài)綁定(調用派生類的重寫版本)。
- 不能直接訪問派生類新增的成員(需顯式類型轉換,如
dynamic_cast)。
- 指針的靜態(tài)類型是
class Base {
public:
virtual void func() { cout << "Base::func()" << endl; }
};
class Derived : public Base {
public:
void func() override { cout << "Derived::func()" << endl; }
void derivedFunc() { cout << "Derived獨有的函數(shù)" << endl; }
};
int main() {
Derived d;
Base* ptr = &d; // 基類指針指向派生類對象
ptr->func(); // 調用Derived::func()(動態(tài)綁定)
// ptr->derivedFunc(); // 錯誤:不能直接訪問派生類新增成員
return 0;
}這里需要注意的是:基類對象不能賦值給派生類對象。
2. 派生類對象賦值給基類引用(Base& ref = derivedObj;)
- 本質:基類引用綁定到派生類對象的基類部分(引用是對象的別名,不產(chǎn)生新對象)。
- 特性:
- 引用的靜態(tài)類型是
Base&,動態(tài)類型是Derived&。 - 行為與基類指針類似:可調用基類成員,調用虛函數(shù)時觸發(fā)動態(tài)綁定。
- 不能直接訪問派生類新增成員(需顯式轉換)。
- 引用的靜態(tài)類型是
int main() {
Derived d;
Base& ref = d; // 基類引用綁定派生類對象
ref.func(); // 調用Derived::func()(動態(tài)綁定)
return 0;
}3. 派生類對象直接賦值給基類對象(Base baseObj = derivedObj;)
- 本質:發(fā)生對象切片(Object Slicing)—— 僅將派生類對象中的基類部分復制到基類對象中,派生類獨有的成員被 "切片" 丟棄。
- 特性:
- 生成一個全新的基類對象(與原派生類對象無關)。
- 該對象的靜態(tài)類型和動態(tài)類型都是
Base,不觸發(fā)多態(tài)(調用函數(shù)時僅使用基類的實現(xiàn))。 - 無法訪問派生類的任何特性(即使顯式轉換也不行,因為派生類部分已丟失)。
int main() {
Derived d;
Base baseObj = d; // 對象切片:僅復制基類部分
baseObj.func(); // 調用Base::func()(無動態(tài)綁定)
return 0;
}
| 方式 | 操作本質 | 是否產(chǎn)生新對象 | 動態(tài)類型 | 是否支持多態(tài)(動態(tài)綁定) | 能否訪問派生類新增成員 |
|---|---|---|---|---|---|
| 派生類→基類指針 | 指針指向派生類的基類部分 | 否 | 派生類類型 | 是(調用虛函數(shù)時) | 否(需顯式轉換) |
| 派生類→基類引用 | 引用綁定派生類的基類部分 | 否 | 派生類類型 | 是(調用虛函數(shù)時) | 否(需顯式轉換) |
| 派生類→基類對象(直接賦值) | 復制基類部分,切片派生部分 | 是(生成基類對象) | 基類類型 | 否 | 否(已被切片丟棄) |
三、隱藏機制
3.1 繼承中的隱藏機制
派生類和基類中有同名成員,派生類成員將屏蔽基類對同名成員的直接訪問,這種情況叫隱藏。(在派生類成員函數(shù)中,要是需要訪問基類中同名成員可以使用基類::基類成員顯式訪問)
class Person
{
protected:
int _num = 111;
string _name = "?李?";
};
class Student : public Person
{
public:
void Print()
{
cout <<_num<<endl;//這里打印結果為999因為Person中的_num被隱藏了
}
protected:
int _num = 999;
};
int main()
{
Student s1;
s1.Print();
return 0;
};需要注意的是對于成員函數(shù)來講,只要函數(shù)名相同就構成了隱藏。也就是說,如果派生類與基類中的成員函數(shù)名相同,通過派生類的實例化對象調用該函數(shù)默認調用派生類中定義的重名函數(shù),需要調用基類中的重名函數(shù)時需要指定類名:
class Person
{
public:
void Print()
{
cout << "我是基類中的Print函數(shù)" << endl;
}
};
class Student : public Person
{
public:
void Print()
{
cout <<"我是派生類中的Print函數(shù)" << endl;
}
};
int main()
{
Student s1;
s1.Print();
return 0;
};
要是想顯式調用基類中的Print函數(shù),需要指定Person::Print();如下示例:
int main()
{
Student s1;
s1.Person::Print();
return 0;
};這也從側面說明,對于繼承關系中的隱藏機制,基類中的重名的成員函數(shù)或者成員變量確確實實被派生類繼承下來了,只是由于隱藏機制默認訪問或調用的是派生類中的成員變量或函數(shù),需要訪問基類中的時需要指明類域。
3.2 重載與隱藏
這里我們試著想一想下面基類與派生類func函數(shù)之間的關系:
A:重載 B:隱藏
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
cout << "func(int i)" << i << endl;
}
};
int main()
{
B b;
b.fun(10);
return 0;
};這里我們發(fā)現(xiàn)基類和派生類中都有一重名的func函數(shù),根據(jù)上述提到的隱藏機制的形成條件基類的func函數(shù)與派生類中的func函數(shù)構成隱藏。
什么是重載?
在 C++ 中,它允許在同一個作用域內定義多個同名的函數(shù)或運算符,但這些同名的函數(shù)或運算符的參數(shù)列表(參數(shù)個數(shù)、參數(shù)類型或參數(shù)順序)必須有所不同,返回值類型可以相同也可以不同。
根據(jù)概念我們就可以發(fā)現(xiàn)兩者的區(qū)別,重載強調在同一作用域也就是同一個類中的同名函數(shù)之間構成重載,繼承中的隱藏機制始終離不開基類與派生類兩個作用域。
四、派生類的默認成員函數(shù)
4.1 構造函數(shù)
派生類的構造函數(shù)必須調用基類的構造函數(shù)初始化基類的那?部分成員。如果基類沒有默認的構造 函數(shù),則必須在派生類構造函數(shù)的初始化列表階段顯式調用。
默認構造函數(shù)是一種特殊的構造函數(shù),它不需要任何參數(shù),或者所有參數(shù)都有默認值。當創(chuàng)建類的對象時如果沒有提供實參,編譯器會自動調用默認構造函數(shù)。
class A
{
public:
//這里基類沒有默認構造函數(shù)需要傳遞參數(shù)
A(string _name)
{
name = _name;
}
string name;
};
class B : public A
{
public:
//派生類中需要顯式調用基類構造函數(shù)初始化基類的成員變量
B(int _n1,string _name)
:A(_name)
,n1(_n1)
{}
void Print()
{
cout << name << n1 << std::endl;
}
private:
int n1;
};
int main()
{
B ss(18,"張三");
ss.Print();
return 0;
};
4.2 拷貝構造函數(shù)
派生類的拷貝構造函數(shù)必須調用基類的拷貝構造完成拷貝初始化。
在一般情況下,拷貝構造函數(shù)編譯器自動生成的就夠用了,但是如果類中有資源需要深拷貝時就需要我們手動實現(xiàn)拷貝構造函數(shù)。
當創(chuàng)建派生類對象時,先調用基類的構造函數(shù),再調用派生類的構造函數(shù)(與析構函數(shù)的調用順序完全相反)。
class A
{
public:
//這里基類沒有默認構造函數(shù)需要傳遞參數(shù)
A(string _name)
{
name = _name;
}
A(const A& _a)
{
name = _a.name;
}
string name;
};
class B : public A
{
public:
B(int _n1,string _name)
:A(_name)
,n1(_n1)
{}
B(const B& _b)
:A(_b)//顯式調用基類的拷貝構造完成構造初始化
,n1(_b.n1)
{}
void Print()
{
cout << name << n1 << std::endl;
}
private:
int n1;
};
int main()
{
B ss(18,"張三");
B pp(ss);
pp.Print();
return 0;
};4.3 operator =
派生類的operator=必須要調用基類的operator=完成基類的復制。需要注意的是派生類的 operator=隱藏了基類的operator=,所以需要指定基類作用域顯式調用基類的operator=。
與前面的拷貝構造一樣,在一般情況下,賦值運算符重載編譯器自動生成的就夠用了,但是如果類中有資源需要深拷貝時就需要我們手動實現(xiàn)賦值運算符重載。
這里我們也來演示一下:
class A
{
public:
//這里基類沒有默認構造函數(shù)需要傳遞參數(shù)
A(string _name)
{
name = _name;
}
A& operator=(A& _a)
{
if (this != &_a)
{
name = _a.name;
}
return *this;
}
string name;
};
class B : public A
{
public:
B(int _n1,string _name)
:A(_name)
,n1(_n1)
{}
B& operator=(B& _b)
{
if (this != &_b)
{
//這里不能是operator(_b)因為構成隱藏會遞歸調用B中的operator=導致棧溢出
A::operator=(_b);
n1 = _b.n1;
}
return *this;
}
void Print()
{
cout << name << n1 << std::endl;
}
private:
int n1;
};
int main()
{
B ss(18,"張三");
B pp=ss;
pp.Print();
return 0;
};
4.4 析構函數(shù)
當派生類對象銷毀時,先調用派生類的析構函數(shù),再調用基類的析構函數(shù)(與構造函數(shù)的調用順序相反)。
派生類的析構函數(shù)會在被調用完成后自動調用基類的析構函數(shù)清理基類成員。因為這樣才能保證派 生類對象先清理派生類成員再清理基類成員的順序。
一般來說,編譯器自動生成的析構函數(shù)就已經(jīng)夠用了,如果有需要顯式釋放的資源,才需要自己實現(xiàn):
#include <iostream>
class Base {
public:
~Base() { std::cout << "Base destructor" << std::endl; }
};
class Derived : public Base {
public:
~Derived() { std::cout << "Derived destructor" << std::endl; }
};
int main() {
Derived d; // 銷毀時先調用 ~Derived(),再調用 ~Base()
return 0;
}
// 輸出:
// Derived destructor
// Base destructor五、繼承與友元
5.1 什么是友元
在 C++ 中,友元(Friend) 是一種特殊的訪問權限機制,它允許一個類或函數(shù)訪問另一個類中的私有(private) 和保護(protected) 成員,即使它們不屬于該類的成員。
通常友元具有以下三種形式:
1. 友元函數(shù)
普通函數(shù)可以被聲明為某個類的友元,從而訪問該類的私有成員。
class MyClass {
private:
int secret;
public:
MyClass(int s) : secret(s) {}
// 聲明友元函數(shù)
friend void printSecret(MyClass obj);
};
// 友元函數(shù)定義(無需加friend關鍵字)
void printSecret(MyClass obj) {
// 可以直接訪問私有成員
cout << "Secret value: " << obj.secret << endl;
}2. 友元類
一個類可以被聲明為另一個類的友元,此時友元類的所有成員函數(shù)都能訪問該類的私有成員。
class A {
private:
int value;
public:
A(int v) : value(v) {}
// 聲明B為A的友元類
friend class B;
};
class B {
public:
void showA(A a) {
// B的成員函數(shù)可以訪問A的私有成員
cout << "A's value: " << a.value << endl;
}
};3. 類的成員函數(shù)作為友元
可以將一個類的特定成員函數(shù)聲明為另一個類的友元(更精確的權限控制)。
class B; // 前向聲明
class A {
public:
void showB(B b);
};
class B {
private:
int data;
public:
B(int d) : data(d) {}
// 僅將A的showB函數(shù)聲明為友元
friend void A::showB(B b);
};
// 實現(xiàn)A的showB函數(shù)
void A::showB(B b) {
cout << "B's data: " << b.data << endl;
}5.2 繼承中的友元關系
這里只需要記住一句話:友元關系不可以繼承,也就是說基類的友元不能訪問派生類的私有或者保護成員:
class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 學號
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
int main()
{
Person p;
Student s;
// 編譯報錯:error C2248 : “Student::_stuNum” :?法訪問protected成員
// 解決?案:Display也變成Student的友元即可
Display(p, s);
return 0;
}
六、繼承與靜態(tài)成員
靜態(tài)成員(變量或函數(shù))屬于整個類,而非某個具體對象,它被該類的所有實例以及所有派生類的實例共享。
在繼承關系中,基類的靜態(tài)成員不會被派生類復制,而是在整個繼承體系中只存在一份副本。
派生類可以直接訪問基類的靜態(tài)成員,但需遵循訪問權限控制(public/protected/private):
- 基類的
public靜態(tài)成員:派生類可直接訪問(通過類名或對象)。 - 基類的
protected靜態(tài)成員:派生類的成員函數(shù)中可訪問,但類外不可直接訪問。 - 基類的
private靜態(tài)成員:派生類無法訪問(即使是成員函數(shù)中)。
#include <iostream>
using namespace std;
class Base {
public:
static int public_static; // 公有靜態(tài)成員
protected:
static int protected_static; // 保護靜態(tài)成員
private:
static int private_static; // 私有靜態(tài)成員
};
// 初始化基類靜態(tài)成員
int Base::public_static = 0;
int Base::protected_static = 0;
int Base::private_static = 0;
class Derived : public Base {
public:
void print() {
// 訪問基類的public靜態(tài)成員
cout << "public_static: " << public_static << endl;
// 訪問基類的protected靜態(tài)成員(僅在派生類內部可訪問)
cout << "protected_static: " << protected_static << endl;
// 錯誤:無法訪問基類的private靜態(tài)成員
// cout << "private_static: " << private_static << endl;
}
};
int main() {
Derived d;
d.print(); // 正確:通過派生類對象調用,訪問基類的public/protected靜態(tài)成員
// 直接訪問基類的public靜態(tài)成員(通過基類名或派生類名)
cout << Base::public_static << endl; // 正確
cout << Derived::public_static << endl; // 正確(派生類共享基類的靜態(tài)成員)
return 0;
}七、多繼承與菱形繼承
7.1 單繼承與多繼承
單繼承指一個派生類只從一個基類繼承成員的方式。這是最簡單、最常用的繼承形式,邏輯清晰,不易產(chǎn)生歧義。
多繼承指一個派生類同時從多個基類繼承成員的方式。它能讓派生類整合多個不同基類的功能,但也可能帶來復雜性。

多繼承的語法格式:
class 基類1 { ... };
class 基類2 { ... };
class 派生類 : 繼承方式 基類1, 繼承方式 基類2, ... {
// 派生類成員
};7.1.1 多繼承中的指針偏移
這里我們先來看一道題:
例題:下面說法正確的是( )
A. p1==p2==p3 B. p1<p2<p3 C.p1==p3!=p2 D.p1!=p2!=p3
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
int main()
{
Derive d;
Base1* p1 = &d;
Base2* p2 = &d;
Derive* p3 = &d;
return 0;
}要解答這道題,我們首先來看指針p3,p3指向整個Derive類:

再看p1與p2,因為p1比p2早聲明所以p1<p2

接下來我們來考慮p1與p3的關系,我們發(fā)現(xiàn)p3與p1分別是派生類與基類的指針,并且p1指向的是第一個基類(最先聲明的)。前面我們講過派生類對象可以賦值給基類的指針/基類的引用,基類指針或引用指向的是派生類中切出來的基類那部分。如圖:

所以正確結果是p2>p1==p3,也就是p1==p3!=p2選C。
7.2 菱形繼承(鉆石問題)
當兩個基類繼承自同一個間接基類,而派生類同時繼承這兩個基類時,會導致間接基類的成員在派生類中存在兩份副本,引發(fā)歧義。

在這里Student類與Teacher類同時繼承了Person類,此時Assistant又同時繼承了Student類與Teacher類,這時會導致Person類中的成員在Assistant類中出現(xiàn)兩次。當我們訪問這類成員時編譯器不知道我們訪問的是Student中Person類的成員還是Teacher中Person類的成員:
如果出現(xiàn)這種情況,我們建議應該聲明要訪問的這類變量的類域讓編譯器知道我們要訪問的是哪一個父類中的重名變量。
class Person
{
public:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _num; //學號
};
class Teacher : public Person
{
protected:
int _id; // 職?編號
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修課程
};
int main()
{
// 編譯報錯:error C2385 :對“_name”的訪問不明確
Assistant a;
a._name = "peter";
// 需要顯?指定訪問哪個基類的成員可以解決?義性問題,但是數(shù)據(jù)冗余問題?法解決
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
return 0;
}7.2.1 虛繼承
虛繼承是面向對象編程中的一種技術,主要用于解決多繼承時可能出現(xiàn)的菱形繼承問題(也稱為鉆石問題)。
當一個派生類同時繼承自兩個基類,而這兩個基類又共同繼承自同一個間接基類時,就會形成菱形繼承結構。這時,派生類會包含間接基類的兩份副本,可能導致數(shù)據(jù)冗余和二義性。
虛繼承的作用就是讓派生類只保留間接基類的一份副本,從而解決上述問題。
在 C++ 中,通過在繼承時使用virtual關鍵字來實現(xiàn)虛繼承,例如:
// 間接基類
class A {
public:
int x;
};
// 虛繼承自A
class B : virtual public A { };
class C : virtual public A { };
// 繼承自B和C,此時A只會有一份副本
class D : public B, public C {
public:
void func() {
x = 10; // 不會產(chǎn)生二義性,因為A只有一份
}
};通過虛繼承,類 D 中只會包含 A 的一個實例,避免了數(shù)據(jù)重復和訪問沖突問題。這種技術在需要實現(xiàn)復雜的多繼承結構時特別有用,例如在一些大型框架的類層次設計中。
八、繼承與組合
繼承允許你根據(jù)基類的實現(xiàn)來定義派生類的實現(xiàn)。這種通過生成派生類的復用通常被稱為白箱復用(white-box reuse)。術語“白箱”是相對可視性而言:在繼方式中,基類的內部細節(jié)對派生類可見。繼承?定程度破壞了基類的封裝,基類的改變,對派生類有很?的影響。派生類和基類間的依賴關系很強,耦合度高。
對象組合是類繼承之外的另?種復用選擇。新的更復雜的功能可以通過組裝或組合對象來獲得。對 象組合要求被組合的對象具有良好定義的接口。這種復用風格被稱為黑箱復用(black-boxreuse), 因為對象的內部細節(jié)是不可見的。對象只以“黑箱”的形式出現(xiàn)。組合類之間沒有很強的依賴關系,耦合度低。優(yōu)先使用對象組合有助于你保持每個類被封裝。
// Tire(輪胎)和Car(?)更符合has - a的關系
class Tire {
protected:
// 品牌
string _brand = "Michelin";
// 尺?
size_t _size = 17;
};
class Car {
protected:
string _colour = "??";
string _num = "陜ABIT00";
Tire _t1;
Tire _t2;
Tire _t3;
Tire _t4;
};
class BMW : public Car {
public:
void Drive()
{
cout << "好開操控" << endl;
}
};
// Car和BMW / Benz更符合is - a的關系
class Benz : public Car {
public:
void Drive()
{
cout << "好坐舒適" << endl;
}
};
template<class T>
class vector
{};
// stack和vector的關系,既符合is - a,也符合has - a
template<class T>
class stack : public vector<T>
{};
template<class T>
class stack
{
public:
vector<T> _v;
};
int main()
{
return 0;
}優(yōu)先使用組合,而不是繼承。實際盡量多去用組合,組合的耦合度低,代碼維護性好。不過也不太 那么絕對,類之間的關系就適合繼承(is-a)那就用繼承,另外要實現(xiàn)多態(tài),也必須要繼承。類之間的 關系既適合用繼承(is-a)也適合組合(has-a),就用組合。
總結
到此這篇關于C++繼承機制的文章就介紹到這了,更多相關C++繼承機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C語言的isatty函數(shù)和ttyname函數(shù)以及sendmsg函數(shù)用法
這篇文章主要介紹了C語言的isatty函數(shù)和ttyname函數(shù)以及sendmsg函數(shù)用法,是C語言入門學習中的基礎知識,需要的朋友可以參考下2015-09-09
Qt顯示QImage圖像在label上,并保持自適應大小問題
這篇文章主要介紹了Qt顯示QImage圖像在label上,并保持自適應大小問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
C/C++實現(xiàn)string和int相互轉換的常用方法總結
在C++編程中,經(jīng)常需要在字符串(string)和整型(int)之間進行轉換,本文將詳細介紹幾種在C和C++中實現(xiàn)這兩種類型轉換的常用方法,有需要的可以參考下2024-01-01

