文章參考:https://blog.csdn.net/war1111886/article/details/8609957
一 .繼承中的訪問權(quán)限關(guān)系
1.基類,父類,超類是指被繼承的類,派生類,子類是指繼承于基類的類.?
2.在C++中使用: 冒號表示繼承,如class A : public B;表示派生類A從基類B繼承而來
3.派生類包含基類的所有成員,而且還包括自已特有的成員,派生類和派生類對象訪問基類中的成員就像訪問自已的成員一樣,可以直接使用,不需加任何操作符,但派生類仍然無法訪問基類中的私有成員.
4.在C++中派生類可以同時從多個基類繼承,Java 不充許這種多重繼承,當繼承多個基類時,使用逗號將基類隔開.
5.基類訪問控制符
? ? ?class A : public B ? ? ? ?基類以公有方式被繼承,
? ? ?class A:private B ? ? ? ?基類以私有方式被繼承,
? ? ?class A:protected B ? ?基類以受保護方式被繼承,如果沒有訪問控制符則默認為私有繼承。
6. protected 受保護的訪問權(quán)限:使用protected 保護權(quán)限表明這個成員是私有的,但在派生類中可以訪問基類中的受保護成員。派生類的對象就不能訪問受保護的成員了。
7. 如果基類以public公有方式被繼承,則基類的所有公有成員都會成為派生類的公有成員.受保護的基類成員成為派生類的受保護成員
? ? ? 如果基類以private私有被繼承,則基類的所有公有成員都會成為派生類的私有成員.基類的受保護成員成為派生類的私有成員.?
? ? ? 如果基類以protected 受保護方式被繼承,那么基類的所有公有和受保護成員都會變成派生類的受保護成員.
? ? ?不管基類以何種方式被繼承,基類的私有成員,仍然保有其私有性,被派生的子類不能訪問基類的私有成員.
例:繼承中的訪問權(quán)限關(guān)系
?
class A {
int a;
protected:
int b;
public:
int c;
A() { a = b = c = 1; }
};
//類B以公有方式從基類A繼承
class B : public A {
public:
int d;
B() {//a=2; //錯誤,不能訪問基類中的私有成員
b = 2; //正確,可以在類中訪問基類中的受保護成員,但類的對象不能訪問,基類中的受保護成員b在類B中仍然是受保護成員
c = d = 2;
}
}; //基類中的公有成員c在類B中仍然是公有成員
//類C以受保護和私有方式從基類A繼承。
class C : protected A {
public:
int e;
C() {//a=3; //錯誤,不能訪問基類中的私有成員
b = c = e = 3;
}
};//這里基類受保護成員b和公有成員c都成為類C中的受保護成員。
class D : private A {
public:
D()
{ b = c = 4; }
};//基類中的公有和受保護成員都成為了類D中的私有成員。
//驗證受保護和私有方式繼承的訪問權(quán)限。
class C1 : public C {
public:
C1() { b = c = e = 4; }
};//正確;類A中的成員b和c在類C中是以受保護方式被繼承的,b和c都成為了類C中的受保護成員。
class D1 : public D {
public:
D1() {//b=5; //錯誤,在A中受保護的成員b在類D中是以私有方式繼承的,這樣b就成為了類D中的私有成員,所以無法訪問。
//c=5; //錯誤,在A中公有的成員c在類D中是以私有方式繼承的,這樣c就成為了類D中的私有成員,所以無法訪問。
}
};
int main()
{
A m1;
B m2;
C m3;
D m4;
//cout<<m1.b<<m2.b<<m3.b<<m4.b<<endl; //錯誤;不能用類的對象訪問受保護的成員,只有在類中才能訪問。
cout << m1.c <<endl;
cout << m2.c <<endl;
//cout<<m3.c<<endl; //錯誤,類C是以受保護的方式從A繼承的,基類中的變量c在類C中就是受保護的,所以類的對象不能訪問
//cout<<m4.c<<endl; //錯誤,類C是以私有的方式從A繼承的,基類中的變量c在類C中就是私有的,所以類的對象不能訪
}
二.覆蓋和隱藏基類成員變量或成員函數(shù)
1. 基類的成員變量或函數(shù)被覆蓋:
如果派生類覆蓋了基類中的成員函數(shù)或成員變量,則當派生類的對象調(diào)用該函數(shù)或變量時是調(diào)用的派生類中的版本,當用基類對象調(diào)用該函數(shù)或變量時是調(diào)用的基類中的版本。
2. 隱藏基類成員函數(shù)的情況:
如果在派生類中定義了一個與基類同名的函數(shù),不管這個函數(shù)的參數(shù)列表是不是與基類中的函數(shù)相同,則這個同名的函數(shù)就會把基類中的所有這個同名的函數(shù)的所有重載版本都隱藏了,這時并不是在派生類中重載基類的同名成員函數(shù),而是隱藏,比如類A中有函數(shù) f (int i , int j)和 f (int i)兩個版本,當在從A派生出的類B中定義了基類的 f() 函數(shù)版本時,這時基類中的 f (int i)和f (int i , int j)就被隱藏了,也就是說由類B創(chuàng)建的對象比如為m,不能直接訪問類A中的f(int i)版本,即使用語句m.f(2)時會發(fā)生錯誤。
3. 怎樣使用派生類的對象訪問基類中被派生類覆蓋或隱藏了的函數(shù)或變量:
? ? ? 3.1. 方法1 ? 使用作用域運算符:: ,在使用對象調(diào)用基類中的函數(shù)或變量時使用作用域運算符即語句m.A::f(2),這時就能訪問基類中的函數(shù)或變量版本。注意,訪問基類中被派生類覆蓋了的成員變量只能用這種方法
? ? ? 3.2.方法2 ? 使用using:: , 該方法只適用于被隱藏或覆蓋的基類函數(shù),在派生類的類定義中使用語句using 把基類的字包含進來,比如using A::f;就是將基類中的函數(shù)f()的所有重載版本包含進來,重載版本被包含到子類之后,這些重載的函數(shù)版本就相當于是子類的一部分,這時就可以用派生類的對象直接調(diào)用被派生類隱藏了的基類版本,比如m.f(2),但是使用這種語句還是沒法調(diào)用基類在派生類中被覆蓋了的基類的函數(shù),比如m.f()調(diào)用的是派生類中定義的函數(shù)f,要調(diào)用被覆蓋的基類中的版本要使用語句m.A::f()才行。
4. 在派生類的函數(shù)中調(diào)用基類中的成員變量和函數(shù)的方法:
就是在函數(shù)中使用的被派生類覆蓋的基類成員變量或函數(shù)前用作域解析符加上基類的類名,即A::f()就是在派生類的函數(shù)中調(diào)用基類中被派生類覆蓋了的函數(shù)f()的方法。
5. 派生類以私有方式被繼承時改變基類中的公有成員為公有的方法:
? ? ? 5.1.使用:: 作用域運算符,不提倡用這種方法,在派生類的public 后面用作用域運算符把基類的公有成員包含進來,這樣基類的成員就會成為派生類中的公有成員了,注意如果是函數(shù)的話后面不能加括號,如A::f;如果f是函數(shù)的話不能有括號。
? ? ? 5.2.使用using語句,現(xiàn)在一般用這種方法,也是在派生類的public使用using把基類成員包函進來,如using A::f。
例:隱藏或覆蓋基類中的成員,使用::作用域運算符訪問
————————————————
版權(quán)聲明:本文為CSDN博主「Sherlock_Homles」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/Sherlock_Homles/article/details/82927515
class A {
int a;
protected:
int b;
public:
int c, d;
void f(int i) {
cout << "class A" << "\n";
}
A() { a = b = c = d = 1; }
};
class B : public A {
public:
int d;
//覆蓋基類中的成員變量d。
B() {
b = c = d = 2; //這里是給子類B中的成員變量d賦值,而不是基類中的d
A::d = 3;
} //給基類中被覆蓋的成員d賦值,注意在類中訪問的被覆蓋成員的方式。
void f() {
cout << "class B" << "\n"; //在子類中重定義基類中的同名函數(shù),雖然參數(shù)列表不一樣,但同樣會隱藏基類中的同名函數(shù)
A::f(1); //在函數(shù)中調(diào)用基類中被隱藏了的同名函數(shù)的方法,使用作用域解析運算符。
//f(1); //錯誤,因為基類中的函數(shù)被子類中的同名函數(shù)隱藏了,在這里子類不知道有一個帶參數(shù)的函數(shù)f。
}
};
int main() {
B m;
cout << m.d << "\n"; //輸出子類中的成員變量d的值,注意派生類中覆蓋了基類成員d.
cout << m.A::d << "\n"; //輸出基類中的成員變量d的值,注意這是使用對象訪問被覆蓋的基類成員的方式
m.f(); //調(diào)用子類中的不帶參數(shù)的函數(shù)f。
// m.f(2); //錯誤,因為基類中的帶一個參數(shù)的函數(shù)f被子類中的同名函數(shù)隱藏掉了,不能這樣訪問,須用作用域解析運算符來訪問。
m.A::f(1);
} //使用子類對象訪問基類中被隱藏的函數(shù)的方法。
例:使用using 語句以便訪問基類中被隱藏的函數(shù)
class A {
int a;
protected:
int b;
public:
int c, d;
void f() { cout << "Amoren" << "\n"; }
void f(int i) { cout << "class A" << "\n"; }
A() { a = b = c = d = 1; }
};
class B : public A {
public:
int d;
//覆蓋基類中的成員變量d。
B() {
b = c = d = 2; //這里是給類B中的成員變量d賦值,而不是基類中的d
A::d = 3;
} //給基類中被覆蓋的成員d賦值,注意在類中訪問的被覆蓋成員的方式。
using A::f; //使用語句using把類A中的函數(shù)f包含進來,以便以后可以直接訪問基類被隱藏了的函數(shù),注意函數(shù)f沒有括號
void f() {
cout << "class B" << "\n"; //在子類中覆蓋基類中的同名函數(shù),注意這里是覆蓋,同時會隱藏基類中的其他同名重載函數(shù)
f(1); //正確,因為使用了using語句,所以可以在類中直接使用基類中f函數(shù)的重載版本。
A::f(2); //正確,雖然使用了using語句,但同樣可以按這種方法訪問基類中的函數(shù)。
A ma;
ma.f(); //正確,在子類中創(chuàng)建的基類對象,可以直接用對象名調(diào)用基類中被子類覆蓋或隱藏了的函數(shù),因為這時不會出現(xiàn)二義性。
ma.f(1); //正確,在子類中創(chuàng)建的基類對象,可以直接用對象名調(diào)用基類中被子類覆蓋或隱藏了的函數(shù),因為這時不會出現(xiàn)二義性。
}
void g() {
cout << "this g" << "\n";
f(); //正確,但該語句訪問的是子類中的不帶參數(shù)函數(shù)f,雖然在類中使用了using語句,但直接調(diào)用被子類覆蓋了的基類函數(shù)時不能使用這種方法
A::f(); //正確,調(diào)用被子類覆蓋了的基類中的函數(shù)f,注意,雖然使用了using但要訪問被子類覆蓋了的函數(shù),只能這樣訪問。
}
};
int main() {
B m;
m.f(); //調(diào)用子類中的不帶參數(shù)的函數(shù),這里不會調(diào)用基類中的不帶參數(shù)的被覆蓋的函數(shù)f。
m.A::f(); //調(diào)用基類中被子類覆蓋了的函數(shù)f,雖然子類使用了using語句,但要訪問基類中被覆蓋的方法只能像這樣使用。
m.f(1); //調(diào)用基類重載的f函數(shù),注意這里可以不用::運算符,因為在子類中使用了using,只要子類沒有覆蓋基類中的方法,都可以這樣直接調(diào)用。
m.A::f(2); //當然,使用了using后,也可以使用這種方法
}
例:派生類以私有方式被繼承時改變基類中的公有成員為公有的方法
class A {
public:
int a, b;
void f() { cout << "f" << "\n"; }
void g() { cout << "g" << "\n"; }
};
class B : private A {
public:
A::f; //使用::運算符使基類中的成員成為公有的。注意函數(shù)名后不能有括號。(C++11標準不允許,使用using A::f)
A::a; //(C++11標準不允許,使用using A::a)
using A::g; //使用using語句使基類中的成員函數(shù)g成為類B中的公有成員,注意函數(shù)名后不能有括號。
};
int main() {
B m;
//m.b=1; //錯誤,因為類B是以私有方式繼承的,類A中的成員在類B中是私有的,這里不能訪問私有成員。
m.f();
m.g();
m.a = 1;
}
三.繼承時的構(gòu)造函數(shù)和析構(gòu)函數(shù)問題
1. 在繼承中,基類的構(gòu)造函數(shù)構(gòu)建對象的基類部分,派生類的構(gòu)造函數(shù)構(gòu)建對象的派生類部分。
2. 當創(chuàng)建派生類對象時先用派生類的構(gòu)造函數(shù)調(diào)用基類的構(gòu)造函數(shù)構(gòu)建基類,然后再執(zhí)行派生類構(gòu)造函數(shù)構(gòu)造派生類。
即先構(gòu)造基類再構(gòu)造派生類的順序。執(zhí)行析構(gòu)函數(shù)的順序與此相反。
3. 調(diào)用基類帶參數(shù)的構(gòu)造函數(shù)的方法:
在派生類的構(gòu)造函數(shù)中使用初始化列表的形式就可以調(diào)用基類帶參數(shù)的構(gòu)造函數(shù)初始化基類成員,如B():A(int i){},類B是類A的派生類。
4. 派生類的構(gòu)造函數(shù)調(diào)用基類的構(gòu)造函數(shù)的方法為:
4.1 如果派生類沒有顯示用初始化列表調(diào)用基類的構(gòu)造函數(shù)時,這時就會用派生類的構(gòu)造函數(shù)調(diào)用基類的默認構(gòu)造
函數(shù),構(gòu)造完基類后,才會執(zhí)行派生類的構(gòu)造函數(shù)函數(shù)體,以保證先執(zhí)行基類構(gòu)造函數(shù)再執(zhí)行派生類構(gòu)造函數(shù)
的順序,如果基類沒有默認構(gòu)造函數(shù)就會出錯。
4.2 如果派生類用 顯示的初始化列表調(diào)用基類的構(gòu)造函數(shù)時,這時就會檢測派生類的初始化列表,當檢測到顯示調(diào)
用基類的構(gòu)造函數(shù)時,就調(diào)用基類的構(gòu)造函數(shù)構(gòu)造基類,然后再構(gòu)造派生類,以保證先執(zhí)行基類構(gòu)造函數(shù)再執(zhí)
行派生類構(gòu)造函數(shù)的順序,如果基類沒有定義派生類構(gòu)造函數(shù)初始化列表調(diào)用的構(gòu)造函數(shù)版本就會出錯。
5. 如果在基類中沒有定義默認構(gòu)造函數(shù),但定義了其他構(gòu)造函數(shù)版本,這時派生類中定義了幾個構(gòu)造函數(shù)的不同版本,
這時只要派生類有一個構(gòu)造函數(shù)沒有顯示調(diào)用基類中定義的構(gòu)造函數(shù)版本就會發(fā)生錯誤,因為編譯器會首先檢查派
生類構(gòu)造函數(shù)調(diào)用基類構(gòu)造函數(shù)的匹配情況,如果發(fā)現(xiàn)不匹配就會出錯,即使沒有創(chuàng)建任何類的對象都會出錯,而
不管這個派生類的對象有沒有調(diào)用派生類的這個構(gòu)造函數(shù)。比如:基類有一個構(gòu)造函數(shù)版本A(int i)而沒有定義默認
構(gòu)造函數(shù),派生類B,有這幾個版本的構(gòu)造函數(shù)B():A(4){},B(int i):A(5){},再有語句B(int i, int j){}沒有顯示調(diào)用
基類定義的構(gòu)造函數(shù)而是調(diào)用基類的默認構(gòu)造函數(shù),如果創(chuàng)建了B m和語句B m(1)時都會提示沒有可用的基類默認
構(gòu)造函數(shù)可用的錯誤,雖然這時類B的對象m沒有調(diào)用派生類B的帶有兩個形參的構(gòu)造函數(shù),但同樣會出錯。
6. 同樣的道理,如果基類中定義了默認構(gòu)造函數(shù),卻沒有其他版本的構(gòu)造函數(shù),而這時派生類卻顯示調(diào)用了基類構(gòu)造
函數(shù)的其他版本,這時就會出錯,不管你有沒有創(chuàng)建類的對象,因為編譯器會先在創(chuàng)建對象前就檢查構(gòu)造函數(shù)的匹
配問題。
7. 派生類只能初始化他的直接基類。比如類C是類B的子類,而類B又是類A的子類,這時class C:public B{public:
B():A(){} };將會出錯,該語句試圖顯示調(diào)用類B的基類類A的構(gòu)造函數(shù),這時會出現(xiàn)類A不是類C的基類的錯誤。
8. 繼承中的復(fù)制構(gòu)造函數(shù)和構(gòu)造函數(shù)一樣,基類的復(fù)制構(gòu)造函數(shù)復(fù)制基類部分,派生類的復(fù)制構(gòu)造函數(shù)復(fù)制派生類部
分。
9.派生類復(fù)制構(gòu)造函數(shù)調(diào)用基類復(fù)制構(gòu)造函數(shù)的方法為:A(const A& m):B(m){}其中B是基類,A是派生類。
10.如果在派生類中定義了復(fù)制構(gòu)造函數(shù)而沒有用初始化列表顯示調(diào)用基類的復(fù)制構(gòu)造函數(shù),這時不管基類是否定義了
復(fù)制構(gòu)造函數(shù),這時出現(xiàn)派生類對象的復(fù)制初始化情況時就將調(diào)用基類中的默認構(gòu)造函數(shù)初始化基類的成員變量,
注意是默認構(gòu)造函數(shù)不是默認復(fù)制構(gòu)造函數(shù),如果基類沒有默認構(gòu)造函數(shù)就會出錯。也就是說派生類的復(fù)制構(gòu)造函
數(shù)的默認隱藏形式是B(const B& j):A(){}這里B是A的派生類,也就是說如果不顯示用初始化列表形式調(diào)用基類的
復(fù)制構(gòu)告函數(shù)時,默認情況下是用初始化列表的形式調(diào)用的是基類的默認構(gòu)造函數(shù)。
11.當在派生類中定義了復(fù)制構(gòu)造函數(shù)且顯示調(diào)用了基類的復(fù)制構(gòu)造函數(shù),而基類卻沒有定義基類的復(fù)制構(gòu)造函數(shù)時,
這時出現(xiàn)派生類對象的復(fù)制初始化情況就將調(diào)用基類中的默認復(fù)制構(gòu)造函數(shù)初始化基類部分,調(diào)用派生類的復(fù)制構(gòu)
造函數(shù)初始化派生類部分,因為復(fù)制構(gòu)造函數(shù)只有一種形式,即A(const A& m){},比如當出現(xiàn)調(diào)用時A(const A&
m):B(m){}如果這時基類B沒有定義復(fù)制構(gòu)造函數(shù),則該語句將會調(diào)用派生類A的默認復(fù)制構(gòu)造函數(shù)。文章來源:http://www.zghlxwxcb.cn/news/detail-615894.html
12.如果基類定義了復(fù)制構(gòu)造函數(shù),而派生類沒有定義時,則會調(diào)用基類的復(fù)制構(gòu)造函數(shù)初始化基類部分,調(diào)用派生類
的默認復(fù)制構(gòu)造函數(shù)初始化派生類部分。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-615894.html
到了這里,關(guān)于C++ | 繼承(基類,父類,超類),(派生類,子類)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!