C++学习笔记:(六)public、protected、private继承详解
前言
上一篇學(xué)習(xí)了繼承的基礎(chǔ)概念以及示例代碼。算是對(duì)繼承有了一個(gè)簡(jiǎn)單的了解。如果想要對(duì)繼承有更深的了解,就要復(fù)習(xí)訪問(wèn)權(quán)限的知識(shí)點(diǎn)。這樣才能深化對(duì)繼承的了解,以及學(xué)習(xí)不同的繼承方式能對(duì)哪些數(shù)據(jù)進(jìn)行操作。
類(lèi)成員包括數(shù)據(jù)成員和函數(shù)成員,分別描述問(wèn)題的屬性和行為,是不可分割的兩個(gè)方面。對(duì)類(lèi)成員訪問(wèn)權(quán)限的控制,是通過(guò)設(shè)置成員的訪問(wèn)控制屬性來(lái)實(shí)現(xiàn)的。訪問(wèn)控制屬性有:公有類(lèi)型、私有類(lèi)型和保護(hù)類(lèi)型。這里簡(jiǎn)單的說(shuō)一下不同類(lèi)型的作用。公有類(lèi)型成員定義了類(lèi)的接口。私有成員只能被本類(lèi)的成員函數(shù)和友元函數(shù)訪問(wèn),來(lái)自類(lèi)外部的任何訪問(wèn)都是非法的。這樣,私有成員就完全隱藏在類(lèi)中,保護(hù)了數(shù)據(jù)的安全性。保護(hù)類(lèi)型成員的性質(zhì)和私有成員的性質(zhì)相似,其差別在于繼承過(guò)程中對(duì)派生類(lèi)影響不同。
類(lèi)中public、protected和private數(shù)據(jù)成員、函數(shù)成員的使用:
#include <iostream> using namespace std;class Asd {public:int x;Asd(int a = 0, int b = 0, int c = 0);int getx(){return x;}int gety(){return y;}void sety1(int b){y = b;}int getz(){return z;}void setz1(int c){z = c;}void sety2(int b){protectedsety(b);}void setz2(int c){privatesetz(c);}void print(){cout << "X:" << x << " " << "Y:" << y << " " << "Z:" << z <<endl;}protected:int y;void protectedsety(int b){y = b;}private:void privatesetz(int c){z = c;}int z; };Asd::Asd(int a, int b, int c) {x = a;y = b;z = c; }int main() {Asd a(3, 2, 3);a.print(); //通過(guò)print()函數(shù)打印a.sety1(3); //通過(guò)public中的函數(shù)設(shè)置ya.setz1(4); //通過(guò)public中的函數(shù)設(shè)置za.print();a.sety2(4); //由protected中的函數(shù)設(shè)置ya.setz2(5); //由private中的函數(shù)設(shè)置zcout << "(x,y,z):";cout << "(" << a.getx() << "," << a.gety() << "," << a.getz() << ")" <<endl; //由public中的函數(shù)得到數(shù)據(jù)cout << "X:" << a.x <<endl; //正確,對(duì)象可以直接訪問(wèn)公有數(shù)據(jù)//cout << "Y:" << a.y <<endl; //錯(cuò)誤,對(duì)象不能直接訪問(wèn)保護(hù)數(shù)據(jù)//cout << "Z:" << a.z <<endl; //錯(cuò)誤,對(duì)象不能直接訪問(wèn)私有數(shù)據(jù)//a.protectedsety(1); //錯(cuò)誤,對(duì)象不能直接訪問(wèn)保護(hù)成員函數(shù)//a.privateserz(1); //錯(cuò)誤,對(duì)象不能直接訪問(wèn)私有成員函數(shù)return 0; }在這個(gè)示例代碼中,每種類(lèi)型都有數(shù)據(jù)成員和函數(shù)成員。可以清楚的看到public中的函數(shù)是如何充當(dāng)接口的。在這個(gè)類(lèi)中,可以通過(guò)public中的函數(shù)直接設(shè)置y、z的值,也可以通過(guò)public中的函數(shù)去訪問(wèn)protected和private中的函數(shù),最終達(dá)到設(shè)置y、z值的目的。在public中的數(shù)據(jù)成員可以通過(guò)”對(duì)象名.數(shù)據(jù)成員”的方式直接使用,而其他類(lèi)型的數(shù)據(jù)成員則不行。
?
接下來(lái)開(kāi)始分析不同的繼承方式:
公有繼承:
#include <iostream> using namespace std;class Point {private:float x;float y;float findx()const{return x;}protected:void setz(int a){z = a;}float z;public:void initPoint(float x = 0, float y = 0, float z = 0){this->x = x;this->y = y;this->z = z;}Point(){}Point(float r){ x = r;}void move(float a, float b){x += a;y += b;}float getx()const{return x;}float gety()const{return y;}float getz()const{return z;}float getprivatex()const{return findx();} };class Rectangle:public Point {private:float w,h;public:void initRectangle(float x, float y, float w, float h){initPoint(x,y);this->w = w;this->h = h;}float geth()const{return h;}float getw()const{return w;}//float publicgetx1()const{return getx();} //正確,getx()是基類(lèi)公有成員函數(shù)//float publicgetx2()const{return findx();} //錯(cuò)誤,findx()是基類(lèi)中的私有成員函數(shù)float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類(lèi)公有成員函數(shù)void publicsetz(int a){setz(a);} //正確 };int main() {Rectangle rect;rect.initRectangle(2,3,10,10);rect.move(3,2);cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;cout << "X:" << rect.publicgetx3() <<endl;rect.publicsetz(20);cout << "Z:" << rect.getz() <<endl;//rect.setz(1); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)保護(hù)成員函數(shù)//rect.findx(); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)私有成員函數(shù)return 0; }(1)基類(lèi)的private、public、protected成員的訪問(wèn)屬性在派生類(lèi)中保持不變。
(2)派生類(lèi)中繼承的成員函數(shù)可以直接訪問(wèn)基類(lèi)中所有成員,派生類(lèi)中新增的成員函數(shù)只能訪問(wèn)基類(lèi)的public和protected成員,不能訪問(wèn)基類(lèi)的private成員。
在上述代碼中,基類(lèi)中的move(),getx(),gety()都被派生類(lèi)繼承了,而且move(),getx()&gety()在基類(lèi)中就是public中的函數(shù)。由(1)可知,派生類(lèi)對(duì)象可以直接使用這些函數(shù)。由派生類(lèi)中新增的三個(gè)publicgetx()函數(shù)可知,派生類(lèi)新增函數(shù)能訪問(wèn)public成員,不能訪問(wèn)private成員。由publicsetz()可知,派生類(lèi)新增函數(shù)能訪問(wèn)protected成員。若想讓派生類(lèi)中的新增成員函數(shù)訪問(wèn)private成員,可以通過(guò)基類(lèi)中public中的函數(shù)間接的訪問(wèn)基類(lèi)的private成員(如:publicgetx3()函數(shù))。可以這樣做,但一般不會(huì)這樣做,因?yàn)樵谧鲰?xiàng)目的時(shí)候是不會(huì)動(dòng)基類(lèi)的。
(3)通過(guò)派生類(lèi)的對(duì)象只能訪問(wèn)基類(lèi)的public成員。(rect.setz(1)和rect.findx()報(bào)錯(cuò)的原因是它們不是基類(lèi)public的成員函數(shù))
?
保護(hù)繼承:
#include <iostream> using namespace std;class Point {private:float x;float y;float findx()const{return x;}protected:void setz(int a){z = a;}float z;public:void initPoint(float x = 0, float y = 0, float z = 0){this->x = x;this->y = y;this->z = z;}Point(){}Point(float r){ x = r;}void move(float a, float b){x += a;y += b;}float getx()const{return x;}float gety()const{return y;}float getz()const{return z;}float getprivatex()const{return findx();} };class Rectangle:protected Point {private:float w,h;public:void initRectangle(float x, float y, float w, float h){initPoint(x,y);//setz(10); //派生類(lèi)可以訪問(wèn)基類(lèi)保護(hù)成員函數(shù)this->w = w;this->h = h;}void move(float a, float b){Point::move(a,b);} //與public繼承的不同float getx()const{return Point::getx();} //與public繼承的不同float gety()const{return Point::gety();} //與public繼承的不同float getz()const{return Point::getz();} //與public繼承的不同float geth()const{return h;}float getw()const{return w;}//float publicgetx1()const{return getx();} //正確,getx()是基類(lèi)公有成員函數(shù)//float publicgetx2()const{return findx();} //錯(cuò)誤,findx()是基類(lèi)中的私有成員函數(shù)float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類(lèi)公有成員函數(shù)void publicsetz(int a){setz(a);} //正確 };int main() {Rectangle rect;rect.initRectangle(2,3,10,10);rect.move(3,2);cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;cout << "X:" << rect.publicgetx3() <<endl;rect.publicsetz(20);cout << "Z:" << rect.getz() <<endl;//rect.setz(1); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)保護(hù)成員函數(shù)//rect.findx(); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)私有成員函數(shù)return 0; }(1)基類(lèi)的public、protected成員都以protected身份出現(xiàn)在派生類(lèi)中。
(2)派生類(lèi)中新增的成員函數(shù)可以直接訪問(wèn)基類(lèi)中的public和protected成員,但不能訪問(wèn)基類(lèi)的private成員。
由(1)可知,基類(lèi)的public成員都以protected身份出現(xiàn)在派生類(lèi)中,所以派生類(lèi)的對(duì)象不能像公有繼承那樣直接使用基類(lèi)中public的函數(shù)。而是要根據(jù)需求添加成員函數(shù)。例如:
void initRectangle(float x, float y, float w, float h) {initPoint(x,y);this->w = w;this->h = h; } void move(float a, float b){Point::move(a,b);} ??????//與public繼承的不同 float getx()const{return Point::getx();} ????????????//與public繼承的不同 float gety()const{return Point::gety();} ????????????//與public繼承的不同 float getz()const{return Point::getz();} ????????????//與public繼承的不同因?yàn)?/span>派生類(lèi)中新增的成員函數(shù)可以直接訪問(wèn)基類(lèi)中的public和protected成員,而且在派生類(lèi)中沒(méi)有initPoint()的同名函數(shù),所以在initRectangle()函數(shù)中可以直接使用initPoint()函數(shù),而不需要寫(xiě)成Point::initPoint();。如果派生類(lèi)對(duì)象要使用getx()函數(shù),那么就要在派生類(lèi)public中定義:
方法一:指明getx()的來(lái)歷 float getx()const{return Point::getx();}。 方法二:為了避免重名可以寫(xiě)成: float protectedgetx()const{return?getx();}。(3)通過(guò)派生類(lèi)的對(duì)象不能訪問(wèn)基類(lèi)的任何成員。(因?yàn)槔^承來(lái)的成員以protected身份出現(xiàn)在派生類(lèi)中)
?
私有繼承:
#include <iostream> using namespace std;class Point {private:float x;float y;float findx()const{return x;}protected:void setz(int a){z = a;}float z;public:void initPoint(float x = 0, float y = 0, float z = 0){this->x = x;this->y = y;this->z = z;}Point(){}Point(float r){ x = r;}void move(float a, float b){x += a;y += b;}float getx()const{return x;}float gety()const{return y;}float getz()const{return z;}float getprivatex()const{return findx();} };class Rectangle:private Point {private:float w,h;public:void initRectangle(float x, float y, float w, float h){initPoint(x,y);this->w = w;this->h = h;}void move(float a, float b){Point::move(a,b);} //與public繼承的不同float getx()const{return Point::getx();} //與public繼承的不同float gety()const{return Point::gety();} //與public繼承的不同float getz()const{return Point::getz();} //與public繼承的不同float geth()const{return h;}float getw()const{return w;}//float publicgetx1()const{return getx();} //正確,getx()是基類(lèi)公有成員函數(shù)//float publicgetx2()const{return findx();} //錯(cuò)誤,findx()是基類(lèi)中的私有成員函數(shù)float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類(lèi)公有成員函數(shù)void publicsetz(int a){setz(a);} };int main() {Rectangle rect;rect.initRectangle(2,3,10,10);rect.move(3,2);cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;cout << "X:" << rect.publicgetx3() <<endl;rect.publicsetz(20);cout << "Z:" << rect.getz() <<endl;//rect.setz(1); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)保護(hù)成員函數(shù)//rect.findx(); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)私有成員函數(shù)return 0; }(1)基類(lèi)的public、protected成員都以private身份出現(xiàn)在派生類(lèi)中。(與保護(hù)繼承的不同)
(2)派生類(lèi)中新增的成員函數(shù)可以直接訪問(wèn)基類(lèi)中的public和protected成員,但不能訪問(wèn)基類(lèi)的private成員。(這里和保護(hù)繼承的情況是一樣的)
(3)通過(guò)派生類(lèi)的對(duì)象不能訪問(wèn)基類(lèi)的任何成員。(因?yàn)槔^承來(lái)的成員以private身份出現(xiàn)在派生類(lèi)中)。可以和上述保護(hù)繼承一樣,通過(guò)新增成員函數(shù)讓對(duì)象使用基類(lèi)的一些操作。
數(shù)據(jù)成員和函數(shù)成員在繼承過(guò)程中以相應(yīng)繼承方式出現(xiàn)在派生類(lèi)中的情況可以這樣理解:
注意:private和protected之間的區(qū)別只有在基類(lèi)派生的類(lèi)中才能表現(xiàn)出來(lái)。派生類(lèi)的成員可以直接訪問(wèn)基類(lèi)的保護(hù)成員,但不能直接訪問(wèn)基類(lèi)的私有成員。因此,對(duì)外而言,保護(hù)成員的行為與私有成員相似;對(duì)內(nèi)而言,保護(hù)成員的行為與公有成員相似。
?
私有繼承與保護(hù)繼承的區(qū)別:
在上述的代碼中,私有繼承和保護(hù)繼承的區(qū)別可能不是很明顯。要想了解的更深,我們可以在Rectangle類(lèi)的基礎(chǔ)上再派生新的類(lèi),在新的派生類(lèi)中看不同的繼承方式對(duì)類(lèi)中各成員的影響。
保護(hù)繼承后再公有繼承:
#include <iostream> using namespace std;class Point {private:float x;float y;float findx()const{return x;}protected:void setz(int a){z = a;}float z;public:void initPoint(float x = 0, float y = 0, float z = 0){this->x = x;this->y = y;this->z = z;}Point(){}Point(float r){ x = r;}void move(float a, float b){x += a;y += b;}float getx()const{return x;}float gety()const{return y;}float getz()const{return z;}float getprivatex()const{return findx();} };class Rectangle:protected Point {private:float w,h;public:void initRectangle(float x, float y, float w, float h){initPoint(x,y);//setz(10); //派生類(lèi)可以訪問(wèn)保護(hù)成員函數(shù)this->w = w;this->h = h;}void move(float a, float b){Point::move(a,b);} //與public繼承的不同float getx()const{return Point::getx();} //與public繼承的不同float gety()const{return Point::gety();} //與public繼承的不同float getz()const{return Point::getz();} //與public繼承的不同float geth()const{return h;}float getw()const{return w;}//float publicgetx1()const{return getx();} //正確,getx()是基類(lèi)公有成員函數(shù)//float publicgetx2()const{return findx();} //錯(cuò)誤,findx()是基類(lèi)中的私有成員函數(shù)float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類(lèi)公有成員函數(shù)void publicsetz(int a){setz(a);} };class A:public Rectangle {private:float a;public:void initA(float x){initRectangle(1,2,3,4);a = x;setz(20); //正確,setz()是Point類(lèi)的protected成員 }//float getx()const{return Rectangle::getx();} //正確 float getx()const{return Point::getx();} //正確 };int main() {Rectangle rect;rect.initRectangle(2,3,10,10);rect.move(3,2);cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;cout << "X:" << rect.publicgetx3() <<endl;rect.publicsetz(20);cout << "Z:" << rect.getz() <<endl;//rect.setz(1); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)保護(hù)成員函數(shù)//rect.findx(); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)私有成員函數(shù)A a;a.initA(10);cout << "A.getx():" << a.getx() <<endl;return 0; }因?yàn)镽ectangle類(lèi)是保護(hù)繼承,Point類(lèi)的public、protected成員都以protected身份出現(xiàn)在派生類(lèi)中,而A是公有繼承的(基類(lèi)各屬性保持不變),所以在A類(lèi)中新增成員函數(shù)可以使用setz()函數(shù)。而且在定義A類(lèi)中g(shù)etx()函數(shù)時(shí),以下兩種形式都是正確的。(改變class A的繼承方式,上面的程序都能正常運(yùn)行)原因是保護(hù)繼承后再公有繼承,A類(lèi)仍然可以訪問(wèn)到Point類(lèi)的成員。
float getx()const{return Rectangle::getx();} ? ? ?? float getx()const{return Point::getx();}私有繼承后再公有繼承:
#include <iostream> using namespace std;class Point {private:float x;float y;float findx()const{return x;}protected:void setz(int a){z = a;}float z;public:void initPoint(float x = 0, float y = 0, float z = 0){this->x = x;this->y = y;this->z = z;}Point(){}Point(float r){ x = r;}void move(float a, float b){x += a;y += b;}float getx()const{return x;}float gety()const{return y;}float getz()const{return z;}float getprivatex()const{return findx();} };class Rectangle:private Point {private:float w,h;public:void initRectangle(float x, float y, float w, float h){initPoint(x,y);this->w = w;this->h = h;}void move(float a, float b){Point::move(a,b);} //與public繼承的不同float getx()const{return Point::getx();} //與public繼承的不同float gety()const{return Point::gety();} //與public繼承的不同float getz()const{return Point::getz();} //與public繼承的不同float geth()const{return h;}float getw()const{return w;}//float publicgetx1()const{return getx();} //正確,getx()是基類(lèi)公有成員函數(shù)//float publicgetx2()const{return findx();} //錯(cuò)誤,findx()是基類(lèi)中的私有成員函數(shù)float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類(lèi)公有成員函數(shù)void publicsetz(int a){setz(a);} };class A:public Rectangle {private:float a;public:void initA(float x){initRectangle(1,2,3,4);a = x;//setz(20); //報(bào)錯(cuò),因?yàn)閟etz()是Point的保護(hù)成員}float getx()const{return Rectangle::getx();} //正確//float getx()const{return Point::getx();} //錯(cuò)誤,不能訪問(wèn)Point類(lèi)的getx() };int main() {Rectangle rect;rect.initRectangle(2,3,10,10);rect.move(3,2);cout << rect.getx() << " " << rect.gety() << " " <<rect.getw() << " " << rect.geth() <<endl;cout << "X:" << rect.publicgetx3() <<endl;rect.publicsetz(20);cout << "Z:" << rect.getz() <<endl;//rect.setz(1); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)保護(hù)成員函數(shù)//rect.findx(); //錯(cuò)誤,派生類(lèi)對(duì)象不能直接訪問(wèn)基類(lèi)私有成員函數(shù)A a;a.initA(10);cout << "A.getx():" << a.getx() <<endl;return 0; }上述代碼可以這樣理解:
結(jié)合上面兩個(gè)代碼和圖片可以看出私有繼承與保護(hù)繼承的區(qū)別。①因?yàn)镽ectangle私有繼承后,基類(lèi)的public、protected成員都以private身份出現(xiàn)在派生類(lèi)中。而setz()函數(shù)是Point類(lèi)中的保護(hù)成員(相當(dāng)于第三種情況),在Rectangle中是私有成員,所以A類(lèi)中不能訪問(wèn)setz()函數(shù)(A的新增成員函數(shù)只能訪問(wèn)私有繼承的Rectangle中的A2,B2,但是setz()相當(dāng)于是B1)。②Rectangle類(lèi)中的getx()函數(shù)在public中,A類(lèi)中的新增成員函數(shù)能訪問(wèn)到(Rectangle的getx()相當(dāng)于是A2),所以float getx()const{return Rectangle::getx();}是正確的。由于Rectangle私有繼承Point,所以A訪問(wèn)不到Point類(lèi)的getx()函數(shù)(Point類(lèi)的getx()相當(dāng)于是A1),所以float getx()const{return Point::getx();}是錯(cuò)誤的,換句話說(shuō)就是私有繼承后再公有繼承的派生類(lèi)訪問(wèn)不到上上個(gè)基類(lèi)的成員。以上兩點(diǎn)就是公有繼承與私有繼承的區(qū)別。
總結(jié)
以上是生活随笔為你收集整理的C++学习笔记:(六)public、protected、private继承详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++学习笔记:(五)继承 多态
- 下一篇: 在Ubuntu下成功搭建以太坊私有链挖矿