C++学习笔记:(六)public、protected、private继承详解
前言
上一篇學習了繼承的基礎概念以及示例代碼。算是對繼承有了一個簡單的了解。如果想要對繼承有更深的了解,就要復習訪問權限的知識點。這樣才能深化對繼承的了解,以及學習不同的繼承方式能對哪些數據進行操作。
類成員包括數據成員和函數成員,分別描述問題的屬性和行為,是不可分割的兩個方面。對類成員訪問權限的控制,是通過設置成員的訪問控制屬性來實現的。訪問控制屬性有:公有類型、私有類型和保護類型。這里簡單的說一下不同類型的作用。公有類型成員定義了類的接口。私有成員只能被本類的成員函數和友元函數訪問,來自類外部的任何訪問都是非法的。這樣,私有成員就完全隱藏在類中,保護了數據的安全性。保護類型成員的性質和私有成員的性質相似,其差別在于繼承過程中對派生類影響不同。
類中public、protected和private數據成員、函數成員的使用:
#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(); //通過print()函數打印a.sety1(3); //通過public中的函數設置ya.setz1(4); //通過public中的函數設置za.print();a.sety2(4); //由protected中的函數設置ya.setz2(5); //由private中的函數設置zcout << "(x,y,z):";cout << "(" << a.getx() << "," << a.gety() << "," << a.getz() << ")" <<endl; //由public中的函數得到數據cout << "X:" << a.x <<endl; //正確,對象可以直接訪問公有數據//cout << "Y:" << a.y <<endl; //錯誤,對象不能直接訪問保護數據//cout << "Z:" << a.z <<endl; //錯誤,對象不能直接訪問私有數據//a.protectedsety(1); //錯誤,對象不能直接訪問保護成員函數//a.privateserz(1); //錯誤,對象不能直接訪問私有成員函數return 0; }在這個示例代碼中,每種類型都有數據成員和函數成員。可以清楚的看到public中的函數是如何充當接口的。在這個類中,可以通過public中的函數直接設置y、z的值,也可以通過public中的函數去訪問protected和private中的函數,最終達到設置y、z值的目的。在public中的數據成員可以通過”對象名.數據成員”的方式直接使用,而其他類型的數據成員則不行。
?
接下來開始分析不同的繼承方式:
公有繼承:
#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()是基類公有成員函數//float publicgetx2()const{return findx();} //錯誤,findx()是基類中的私有成員函數float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類公有成員函數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); //錯誤,派生類對象不能直接訪問基類保護成員函數//rect.findx(); //錯誤,派生類對象不能直接訪問基類私有成員函數return 0; }(1)基類的private、public、protected成員的訪問屬性在派生類中保持不變。
(2)派生類中繼承的成員函數可以直接訪問基類中所有成員,派生類中新增的成員函數只能訪問基類的public和protected成員,不能訪問基類的private成員。
在上述代碼中,基類中的move(),getx(),gety()都被派生類繼承了,而且move(),getx()&gety()在基類中就是public中的函數。由(1)可知,派生類對象可以直接使用這些函數。由派生類中新增的三個publicgetx()函數可知,派生類新增函數能訪問public成員,不能訪問private成員。由publicsetz()可知,派生類新增函數能訪問protected成員。若想讓派生類中的新增成員函數訪問private成員,可以通過基類中public中的函數間接的訪問基類的private成員(如:publicgetx3()函數)。可以這樣做,但一般不會這樣做,因為在做項目的時候是不會動基類的。
(3)通過派生類的對象只能訪問基類的public成員。(rect.setz(1)和rect.findx()報錯的原因是它們不是基類public的成員函數)
?
保護繼承:
#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); //派生類可以訪問基類保護成員函數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()是基類公有成員函數//float publicgetx2()const{return findx();} //錯誤,findx()是基類中的私有成員函數float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類公有成員函數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); //錯誤,派生類對象不能直接訪問基類保護成員函數//rect.findx(); //錯誤,派生類對象不能直接訪問基類私有成員函數return 0; }(1)基類的public、protected成員都以protected身份出現在派生類中。
(2)派生類中新增的成員函數可以直接訪問基類中的public和protected成員,但不能訪問基類的private成員。
由(1)可知,基類的public成員都以protected身份出現在派生類中,所以派生類的對象不能像公有繼承那樣直接使用基類中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繼承的不同因為派生類中新增的成員函數可以直接訪問基類中的public和protected成員,而且在派生類中沒有initPoint()的同名函數,所以在initRectangle()函數中可以直接使用initPoint()函數,而不需要寫成Point::initPoint();。如果派生類對象要使用getx()函數,那么就要在派生類public中定義:
方法一:指明getx()的來歷 float getx()const{return Point::getx();}。 方法二:為了避免重名可以寫成: float protectedgetx()const{return?getx();}。(3)通過派生類的對象不能訪問基類的任何成員。(因為繼承來的成員以protected身份出現在派生類中)
?
私有繼承:
#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()是基類公有成員函數//float publicgetx2()const{return findx();} //錯誤,findx()是基類中的私有成員函數float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類公有成員函數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); //錯誤,派生類對象不能直接訪問基類保護成員函數//rect.findx(); //錯誤,派生類對象不能直接訪問基類私有成員函數return 0; }(1)基類的public、protected成員都以private身份出現在派生類中。(與保護繼承的不同)
(2)派生類中新增的成員函數可以直接訪問基類中的public和protected成員,但不能訪問基類的private成員。(這里和保護繼承的情況是一樣的)
(3)通過派生類的對象不能訪問基類的任何成員。(因為繼承來的成員以private身份出現在派生類中)。可以和上述保護繼承一樣,通過新增成員函數讓對象使用基類的一些操作。
數據成員和函數成員在繼承過程中以相應繼承方式出現在派生類中的情況可以這樣理解:
注意:private和protected之間的區別只有在基類派生的類中才能表現出來。派生類的成員可以直接訪問基類的保護成員,但不能直接訪問基類的私有成員。因此,對外而言,保護成員的行為與私有成員相似;對內而言,保護成員的行為與公有成員相似。
?
私有繼承與保護繼承的區別:
在上述的代碼中,私有繼承和保護繼承的區別可能不是很明顯。要想了解的更深,我們可以在Rectangle類的基礎上再派生新的類,在新的派生類中看不同的繼承方式對類中各成員的影響。
保護繼承后再公有繼承:
#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); //派生類可以訪問保護成員函數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()是基類公有成員函數//float publicgetx2()const{return findx();} //錯誤,findx()是基類中的私有成員函數float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類公有成員函數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類的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); //錯誤,派生類對象不能直接訪問基類保護成員函數//rect.findx(); //錯誤,派生類對象不能直接訪問基類私有成員函數A a;a.initA(10);cout << "A.getx():" << a.getx() <<endl;return 0; }因為Rectangle類是保護繼承,Point類的public、protected成員都以protected身份出現在派生類中,而A是公有繼承的(基類各屬性保持不變),所以在A類中新增成員函數可以使用setz()函數。而且在定義A類中getx()函數時,以下兩種形式都是正確的。(改變class A的繼承方式,上面的程序都能正常運行)原因是保護繼承后再公有繼承,A類仍然可以訪問到Point類的成員。
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()是基類公有成員函數//float publicgetx2()const{return findx();} //錯誤,findx()是基類中的私有成員函數float publicgetx3()const{return getprivatex();} //正確,getprivatex()是基類公有成員函數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的保護成員}float getx()const{return Rectangle::getx();} //正確//float getx()const{return Point::getx();} //錯誤,不能訪問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); //錯誤,派生類對象不能直接訪問基類保護成員函數//rect.findx(); //錯誤,派生類對象不能直接訪問基類私有成員函數A a;a.initA(10);cout << "A.getx():" << a.getx() <<endl;return 0; }上述代碼可以這樣理解:
結合上面兩個代碼和圖片可以看出私有繼承與保護繼承的區別。①因為Rectangle私有繼承后,基類的public、protected成員都以private身份出現在派生類中。而setz()函數是Point類中的保護成員(相當于第三種情況),在Rectangle中是私有成員,所以A類中不能訪問setz()函數(A的新增成員函數只能訪問私有繼承的Rectangle中的A2,B2,但是setz()相當于是B1)。②Rectangle類中的getx()函數在public中,A類中的新增成員函數能訪問到(Rectangle的getx()相當于是A2),所以float getx()const{return Rectangle::getx();}是正確的。由于Rectangle私有繼承Point,所以A訪問不到Point類的getx()函數(Point類的getx()相當于是A1),所以float getx()const{return Point::getx();}是錯誤的,換句話說就是私有繼承后再公有繼承的派生類訪問不到上上個基類的成員。以上兩點就是公有繼承與私有繼承的區別。
總結
以上是生活随笔為你收集整理的C++学习笔记:(六)public、protected、private继承详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++学习笔记:(五)继承 多态
- 下一篇: s3c2440移植MQTT