一、基本概念
1、類的繼承,是新的類從已有類那里得到已有的特性,或從已有類產生新類的程序就是類的派生,原有的類稱為基類或父類,產生的新類稱為派生類或子類, 2、派生類的宣告: class 派生類名:繼承方式 基類名1, 繼承方式 基類名2,...,繼承方式 基類名n { 派生類成員宣告; }; 3、一個派生類可以同時有多個基類,這種情況稱為多重繼承,派生類只有一個基類,稱為單繼承,直接派生,間接派生, 4、繼承方式規定了如何訪問基類繼承的成員,繼承方式有public, private, protected,如果不顯示給出繼承方式,默認為private繼承,繼承方式指定了派生類成員以及類外物件對于從基類繼承來的成員的訪問權限, 5、派生類繼承基類中除構造和析構函式以外的所有成員, 6、派生類生成: 吸識訓類成員(除構造解構式以外的所有成員); 改造基類成員(根據繼承方式調整基類成員的訪問,函式在子類中的覆寫,以及虛函式在子類中的覆寫); 添加新的成員; 7、公有繼承 當類的繼承方式為公有繼承時,基類的公有和保護成員的訪問屬性在派生類中不變,而基類的私有成員不可訪問,即基類的公有成員和保護成員被繼承到派生類中仍作為派生類的公有成員和保護成員,派生類的其他成員可以直接訪問它們,無論派生類的成員還是派生類的物件都無法訪問基類的私有成員, 8、私有繼承 當類的繼承方式為私有繼承時,基類中的公有成員和保護成員都以私有成員身份出現在派生類中,而基類的私有成員在派生類中不可訪問,基類的公有成員和保護成員被繼承后作為派生類的私有成員,派生類的其他成員可以直接訪問它們,但是在類外部通過派生類的物件無法訪問,無論是派生類的成員還是通過派生類的物件,都無法訪問從基類繼承的私有成員,通過多次私有繼承后,對于基類的成員都會成為不可訪問,因此私有繼承比較少用, 9、保護繼承 保護繼承中,基類的公有成員和私有成員都以保護成員的身份出現在派生類中,而基類的私有成員不可訪問,派生類的其他成員可以直接訪問從基類繼承來的公有和保護成員,但是類外部通過派生類的物件無法訪問它們,無論派生類的成員還是派生類的物件,都無法訪問基類的私有成員, 二、派生類的建構式和解構式 1、派生類中由基類繼承而來的成員的初始化作業還是由基類的建構式完成,然后派生類中新增的成員在派生類的建構式中初始化, 2、派生類建構式的語法: 派生類名::派生類名(引數總表):基類名1(引數表1),基類名(引數名2)....基類名n(引數名n),內嵌子物件1(引數表1),內嵌子物件2(引數表2)....內嵌子物件n(引數表n) { 派生類新增成員的初始化陳述句; } 注:建構式的初始化順序并不以上面的順序進行,而是根據宣告的順序初始化, 3、如果基類中沒有不帶引數的建構式,那么在派生類的建構式中必須呼叫基類建構式,以初始化基類成員, 4、派生類建構式執行的次序: 呼叫基類建構式,呼叫順序按照它們被繼承時宣告的順序(從左到右); 呼叫內嵌成員物件的建構式,呼叫順序按照它們在類中宣告的順序; 派生類的建構式體中的內容, 例子:#include <iostream>
#include <time.h>
using namespace std;
class B1
{
public:
B1(int i)
{
cout<<"constructing B1 "<<i<<endl;
}
};
class B2
{
public:
B2(int j)
{
cout<<"constructing B2 "<<j<<endl;
}
};
class B3
{
public:
B3()
{
cout<<"constructing B3"<<endl;
}
};
class C: public B2, public B1, public B3
{
public:
C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b)
{
}
private:
B1 memberB1;
B2 memberB2;
B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
return 0;
}
輸出結果為:
constructing B2 2
constructing B1 1
constructing B3
constructing B1 3
constructing B2 4
constructing B3
5、解構式
派生類的解構式的功能是在該物件消亡之前進行一些必要的清理作業,解構式沒有型別,也沒有引數,解構式的執行順序與建構式相反,
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B1
{
public:
B1(int i)
{
cout<<"constructing B1 "<<i<<endl;
}
~B1()
{
cout<<"destructing B1"<<endl;
}
};
class B2
{
public:
B2(int j)
{
cout<<"constructing B2 "<<j<<endl;
}
~B2()
{
cout<<"destructing B2"<<endl;
}
};
class B3
{
public:
B3()
{
cout<<"constructing B3"<<endl;
}
~B3()
{
cout<<"destructing B3"<<endl;
}
};
class C: public B2, public B1, public B3
{
public:
C(int a, int b, int c, int d):B1(a), memberB2(d), memberB1(c),B2(b)
{
}
private:
B1 memberB1;
B2 memberB2;
B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
return 0;
}
輸出結果為:
constructing B2 2
constructing B1 1
constructing B3
constructing B1 3
constructing B2 4
constructing B3
destructing B3
destructing B2
destructing B1
destructing B3
destructing B1
destructing B2
三、派生類成員的標識和訪問
1、派生類成員屬性劃分為四種:
不可訪問的成員;私有成員;保護成員;公有成員;
2、作用域分辨
形式為:基類名::成員名;基類名::成員名(引數表);
如果某派生類的多個基類擁有同名的成員,同時,派生類又新增這樣的同名成員,在這種情況下,派生類成員將覆寫所有基類的同名成員,這就需要這樣的呼叫方式才能呼叫基類的同名成員,
例子:多繼承同名
#include <iostream>
#include <time.h>
using namespace std;
class B1
{
public:
int nV;
void fun()
{
cout<<"member of B1 "<<nV<<endl;
}
};
class B2
{
public:
int nV;
void fun()
{
cout<<"member of B2 "<<nV<<endl;
}
};
class D1: public B1, public B2
{
public:
int nV;
void fun()
{
cout<<"member of D1 "<<nV<<endl;
}
};
int main()
{
D1 d1;
d1.nV = 1;
d1.fun();
d1.B1::nV = 2;
d1.B1::fun();
d1.B2::nV = 3;
d1.B2::fun();
return 0;
}
輸出結果為:
member of D1 1
member of B1 2
member of B2 3
以上通過作用域分辨符,解決了訪問基類中被屏蔽的同名成員,
3、如果某個派生類的部分或全部直接基類是從另一個共同的基類派生而來,在這些直接基類中,從上一級基類繼承來的成員就擁有相同的名稱,因此派生類中也就會產生同名現象,對這種型別的同名成員也要使用作用域分辨符來唯一標識,而且必須用直接基類進行限定,
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
int nV;
void fun()
{
cout<<"member of B0 "<<nV<<endl;
}
};
class B1:public B0
{
public:
int nV1;
};
class B2:public B0
{
public:
int nV2;
};
class D1:public B1, public B2
{
public:
int nVd;
void fund()
{
cout<<"member of D1"<<endl;
}
};
int main()
{
D1 d1;
d1.B1::nV = 2;
d1.B1::fun();
d1.B2::nV = 3;
d1.B2::fun();
return 0;
}
輸出結果為:
member of B0 2
member of B0 3
在這種情況下,派生類物件在記憶體中就同時擁有成員nV及fun的兩份拷貝,但是很多情況下,我們只需要這樣一個這樣的資料拷貝,同一成員的多份拷貝增加了記憶體的開銷,可以通過虛函式來解決這個問題,
4、虛基類
為了解決前面提到的多重拷貝的問題,可以將共同基類設定為虛基類,這時從不同的路徑繼承過來的同名資料成員在記憶體中就只有一個拷貝,同一個函式也只有一個映射,
虛基類的宣告是在派生類的宣告程序,其語法形式為:
class 派生類名::virtual 繼承方式 基類名;
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
int nV;
void fun()
{
cout<<"member of B0 "<<nV<<endl;
}
};
class B1:virtual public B0
{
public:
int nV1;
};
class B2:virtual public B0
{
public:
int nV2;
};
class D1:public B1, public B2
{
public:
int nVd;
void fund()
{
cout<<"member of D1"<<endl;
}
};
int main()
{
D1 d1;
d1.nV = 2;
d1.fun();
return 0;
}
輸出結果為:
member of B0 2
5、虛基類及其派生類的建構式
一般而言,派生類只對其直接基類的建構式傳遞引數,但是在虛基類中,不管是直接或間接虛基類的所有派生類,都必須在建構式的成員初始化串列中列出對虛基類的初始化,
例子:
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
B0(int n)
{
nV = n;
}
int nV;
void fun()
{
cout<<"member of B0 "<<nV<<endl;
}
};
class B1:virtual public B0
{
public:
B1(int a):B0(a)
{
}
int nV1;
};
class B2:virtual public B0
{
public:
B2(int a):B0(a)
{
}
int nV2;
};
class D1:public B1, public B2
{
public:
D1(int a):B0(a), B1(a), B2(a)
{
}
int nVd;
void fund()
{
cout<<"member of D1"<<endl;
}
};
int main()
{
D1 d1(1);
d1.nV = 2;
d1.fun();
return 0;
}
以上例子看上去B0的建構式好像被呼叫了三次,但是實際上只有D1類中的D1(int a):B0(a), B1(a), B2(a)才是真正的呼叫了B0建構式, 四、賦值兼容規則 1、賦值兼容規則是指在需要基類物件的任何地方都可以使用公有派生類的物件來替代, 2、賦值兼容規則中所指的替代包括: 派生類的物件可以賦值給基類物件; 派生類的物件可以初始化基類的參考; 派生類物件的地址可以賦給指向基類的指標, 在替代之后,派生類物件就可以作為基類的物件使用,但只能使用從基類繼承的成員, 例子:
#include <iostream>
#include <time.h>
using namespace std;
class B0
{
public:
void display()
{
cout<<"B0::display()"<<endl;
}
};
class B1:public B0
{
public:
void display()
{
cout<<"B1::display()"<<endl;
}
};
class B2:public B0
{
public:
void display()
{
cout<<"B2::display()"<<endl;
}
};
void fun(B0 *ptr)
{
ptr->display();
}
int main()
{
B0 b0;
B1 b1;
B2 b2;
fun(&b0);
b0 = b1;
fun(&b0);
b0 = b2;
fun(&b0);
return 0;
}
輸出結果為:
B0::display()
B0::display()
B0::display()
通過這種賦值兼容后,每次呼叫的同名函式都是基類的同名函式,如果想呼叫派生類的,則需要使用虛函式,
五、總結

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/103669.html
標籤:C++
上一篇:運算子多載(二)
下一篇:c++ const問題小記
