当前位置:首页 > 公众号精选 > 嵌入式微处理器
[导读]继承和派生的概念 0 1 派生 通过特殊化已有的类来建立新类的过程,叫做“类的派生”, 原有的类叫做”基类”,新建立的类叫做“派生类”。 0 2 继承 类的继承是指派生类继承基类的数据成员和成员函数。继承用来表示类属关系,不能将继承理解为构成关系。 0 3






继承和派生的概念






0 1
派生



通过特殊化已有的类来建立新类的过程,叫做“类的派生”, 原有的类叫做”基类”,新建立的类叫做“派生类”。



0 2
继承



类的继承是指派生类继承基类的数据成员和成员函数。继承用来表示类属关系,不能将继承理解为构成关系。



0 3
继承派生的作用



  • 增加新的成员(数据成员和成员函数)

  • 重新定义已有的成员函数

  • 改变基类成员的访问权限






单一继承






0 1
一般形式



   代码格式:

 class 派生类名: 访问控制 基类名 {         private: 成员声明列表         protected: 成员声明列表         public: 成员声明列表 }

“冒号表示新类是哪个基类的派生类;访问控制指继承方式。

 

三个方式:public、protected、private



0 2
派生类的构造函数和析构函数



// 基类class Point { int x; int y;  public: Point(int a, int b) { x = a; y = b; cout << "init Point" << endl; } void showPoint() { cout << "x = " << x << ", y = " << y << endl; } ~Point() { cout << "delete Point" << endl; }};
// 派生类class Rect: public Point { int w; int h; public: // 调用基类的构造函数对基类成员进行初始化 Rect(int a, int b, int c, int d):Point(a, b) { w = c; h = d; cout << "init Rect" << endl; } void showRect() { cout << "w = " << w << ", h = " << h << endl; } ~Rect() { cout << "delete Rect" << endl; }};
int main() { Rect r(3, 4, 5, 6); r.showPoint(); r.showRect(); /** 输出结果 init Point // 当定义一个派生类的对象时, 首先调用基类的构造函数, 完成对基类成员的初始化 init Rect // 然后执行派生类的构造函数, 完成对派生类成员的初始化 x = 3, y = 4 // 调用基类成员函数showPoint(); w = 5, h = 6 // 调用派生类成员函数showRect(); delete Rect // 构造函数的执行顺序和构造函数的执行顺序相反, 首先调用派生类的析构函数 delete Point // 其次调用基类的析构函数 */}



0 3
类的保护成员



如果希望Rect中的showRect()函数可以一次显示x、y、w、h。我们直接修改showRect()函数是不行的。

void showRect() { cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;}

报错 error: 'x' is a private member of‘Point' 'y' is a private member of‘Point'  


x, y为Point类的私有成员,公有派生时,在Rect类中是不可访问的。


我们还需要将基类Point中的两个成员声明为protected的属性


像这样:

// 基类class Point { // 公有数据成员 protected: int x; int y;  public: Point(int a, int b) { x = a; y = b; cout << "init Point" << endl; } void showPoint() { cout << "x = " << x << ", y = " << y << endl; }};
// 派生类class Rect: public Point { int w; int h; public: // 调用基类的构造函数对基类成员进行初始化 Rect(int a, int b, int c, int d):Point(a, b) { w = c; h = d; cout << "init Rect" << endl; }     /** 公有派生, Point类中的受保护数据成员, 在Rect类中也是受保护的, 所以可以访问  // 而通过公有继承的基类私有的成员, 在派生类中是不可被访问的    void showRect() {        cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl;    }*/};
int main() { Rect r(3, 4, 5, 6); r.showPoint(); r.showRect();}



0 4
访问权限和赋值兼容规则



根类中,对于成员的访问级别有三种:public、protected、private


派生类中,对于成员的访问级别有四种:public(公有)、protected(受保护)、private(私有)、inaccessible(不可访问)


(1)公有派生和赋值兼容规则


公有派生


基类成员的访问权限在派生类中基本保持不变。


  • 基类的公有成员在派生类中仍然是公有的

  • 基类的保护成员在派生类中仍然是受保护的

  • 基类的不可访问的成员在派生类中仍然是不可访问的

  • 基类的私有成员在派生类中变成了不可访问的


总结:在公有派生的情况下,通过派生类自己的成员函数可以访问继承过来的公有和保护成员, 但是不能访问继承来的私有成员, 因为继承过程的私有成员,变成了第四个级别,不可访问的。


赋值兼容规则:


在公有派生的情况下, 一个派生类的对象可以作为基类的对象来使用的情况。


像这样:

// 基类class Point { // 这里声明成员属性为受保护的 protected: int x; int y;  public: Point(int a, int b) { x = a; y = b; }  void show() { cout << "x = " << x << ", y = " << y << endl; }};
// 派生类class Rect: public Point { int w; int h; public: // 调用基类的构造函数对基类成员进行初始化 Rect(int a, int b, int c, int d):Point(a, b) { w = c; h = d; } void show() { cout << "x = " << x << ", y = " << y << ", w = " << w << ", h = " << h << endl; }};
int main() { Point a(1, 2); Rect b(3, 4, 5, 6); a.show(); b.show(); Point & pa = b; // 派生类对象初始化基类的引用 pa.show(); // 实际调用基类的show()函数 Point * p = &b; // 派生类对象的地址赋值给指向基类的指针 p -> show(); // 实际也是调用基类的show()函数 Rect * pb = &b; // 派生类指针 pb -> show(); // 调用派生类的show()函数 a = b; // 派生类对象的属性值, 更新基类对象的属性值 a.show(); // 调用基类的show()函数 /** x = 1, y = 2 x = 3, y = 4, w = 5, h = 6 x = 3, y = 4 x = 3, y = 4 x = 3, y = 4, w = 5, h = 6 x = 3, y = 4 */}


(2)“isa”和”has-a“的区别


继承和派生 isa


比如一个Person类,派生出一个Student类,我们可以说Student就是Person,也就是 Student isa Person,而反过来则不行。


一个类用另一个类的对象作为自己的数据成员或者成员函数的参数 has-a。


像这样:

// 地址类class Address {};class PhoneNumber {};
// 职工类class Worker { String name; Address address; PhoneNumber voiceNumber;};

表示一个Worker对象有一个名字,一个地址,一个电话号码,has-a的关系,包含的关系。


(3)私有派生


通过私有派生,基类的私有和不可访问成员在派生类中是不可访问的,而公有和保护成员这里就成了派生类的私有成员。

// 基类class Point { int x; int y;  public: Point(int a, int b) { x = a; y = b; }
void show() { cout << "x = " << x << ", y = " << y << endl; }};
// 派生类class Rect: private Point { int w; int h; public: Rect(int a, int b, int c, int d):Point(a, b) { w = c; h = d; } void show() { Point::show(); // 通过私有继承, Point类中的公有成员show(), 在Rect中为私有 cout << "w = " << w << ", h = " << h << endl; }};
class Test: public Rect { public: Test(int a, int b, int c, int d):Rect(a, b, c, d) { } void show() { Rect::show();        //Point::show(); /** error: 'Point' is a private member of ‘Point’ 标明: 不可访问基类Point中的成员 Rect类私有继承自Point类, 所以Point中的私有成员x, 私有成员y, 在Rect类中为不可访问: Point类中公有成员show(), 在Rect中变私有 Test类公有继承自Rect类, 所以在Rect中成员x, 成员y, 仍然是不可访问, Rect::show()还是public, 但是Point::show()不可访问 */ }};

因为私有派生不利于进一步派生, 因而实际中私有派生用得并不多。


(4)保护派生保护派生使原来的权限都降一级使用


即private变为不可访问,protected变为private,public变为protected。


限制了数据成员和成员函数的访问权限,因此在实际中保护派生用得也不多


比如:我们在上个例子中,Rect类保护派生于Point,则在Test类中Point::show();就可以使用啦!






多重继承






0 1
一个类从多个基类派生



代码格式:

class 派生类名: 访问控制 基类名1访问控制 基类名2… { //定义派生类自己的成员}


像这样:

// 基类A, 也叫根类class A { int a;  public: void setA(int x) { a = x; }  void showA() { cout << "a = " << a << endl; }};
// 基类B, 也叫根类class B { int b; public: void setB(int x) { b = x; } void showB() { cout << "b = " << b << endl; }};
// 多重继承, 公有继承自类A, 私有继承自类Bclass C: public A, private B { int c; public: void setC(int x, int y) { c = x; setB(y); } void showC() { showB(); cout << "c = " << c << endl; }};
int main() { C c; c.setA(53); // 调用基类setA()函数 c.showA(); // 调用基类showA()函数 c.setC(55, 58); // 调用派生类C的setC()函数 c.showC(); // 调用派生类C的showC()函数 // 派生类C私有继承自基类B, 所以基类B中私有成员b, 在派生类C中不可访问, 基类B中公有成员setB(), showB()在派生类C中变私有. 在main()函数中不可访问    // c.setB(60); // error: 'setB' is a private member of 'B'    // c.showB(); // 'showB' is a private member of 'B' /** a = 53 b = 58 c = 55 */}






二义性及其支配规则





对基类成员的访问必须是无二义性的,如果一个表达式的含义可以解释为可以访问多个基类中的成员,则这种对基类成员的访问就是不确定的,称这种访问具有二义性



0 1
作用域分辨符和成员名限定



代码格式:

类名::标识符

:: 为作用域分辨符,"类名"可以是任一基类或派生类名,“标识符”是该类中声明的任一成员名,


像这样:

// 基类A, 也叫根类class A { public: void func() { cout << "A func" << endl; }};
// 基类B, 也叫根类class B { public: void func() { cout << "B func" << endl; } void gunc() { cout << "B gunc" << endl; }};
// 多重继承class C: public A, public B { public: void gunc() { cout << "C gunc" << endl; } void hunc() { /** 这里就具有二义性, 它即可以访问A类中的func(), 也可以访问类B中的func() */ //func(); // error: Member 'func' found in multiple base classes of different types } void hunc1() { A::func(); } void hunc2() { B::func(); }};
int main() { C c;    //c.func(); //具有二义性 c.A::func(); c.B::func(); c.B::gunc(); c.C::gunc(); c.gunc(); c.hunc1(); c.hunc2(); /** 输出结果 A func B func B gunc C gunc C gunc // 如果基类中的名字在派生类中再次声明, 则基类中的名字就被隐藏. 如果我们想要访问被隐藏的基类中的成员则使用作用域分辨符B::gunc(); A func B func */}



0 2
派生类支配基类的同名函数



如果派生类定义了一个同基类成员函数同名的新成员函数(具有相同参数表的成员函数),派生类的新成员函数就覆盖了基类的同名成员函数。


在这里,直接使用成员名只能访问派生类中的成员函数,使用作用域运算符,才能访问基类的同名成员函数


派生类中的成员函数名支配基类中的同名的成员函数名,这称为名字支配规则。


如果一个名字支配另一个名字,则二者之间不存在二义性,当选择该名字时,使用支配者的名字。


例如上个例子中

c.gunc() // 输出”C gunc”, 基类B中的gunc成员函数被支配了c.B::gunc(); // 加上作用域分辨符, 来使用被支配的成员






总结





C++继承可能更灵活, 并且支持三种派生方式。

我们在学习一门语言的时候, 更应该把精力放在它的特性上面, 不应该用什么语言, 都用自己所擅长语言的思考方式, 实现方式等, 要学会发挥该语言的优势所在。


-END-


来源 :老九学堂




推荐阅读



【01】C/C++干货:函数指针与指针函数
【02】C++之智能指针的学习总结
【03】史上最全!一文让你学完C++,干货收藏!!!
【04】C++之标准库的学习总结
【05】C++基础知识!初学者必看!


免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

嵌入式ARM

扫描二维码,关注更多精彩内容

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

全世界数以百万计的工程师和科学家都在使用 MATLAB® 分析和设计改变着我们的世界的系统和产品。基于矩阵的 MATLAB 语言是世界上表示计算数学最自然的方式。

关键字: matlab入门 教学 基础知识

MATLAB是美国MathWorks公司出品的商业数学软件,用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人,控制系统等领域。

关键字: matlab入门 命令 基础知识

MATLAB是美国MathWorks公司出品的商业数学软件,用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理、量化金融与风险管理、机器人,控制系统等领域。

关键字: matlab入门 基础知识 信号处理

上海2023年2月14日 /美通社/ -- 步入2023年第一个季度,市场依然在震荡中前行。作为一家全球领先的金融科技经纪商,ATFX为了给全球客户提供具有前瞻性的分析报告,分析师团队早已密切关注金融市场动态,结合各国经...

关键字: ANTI RAM 基础知识 SIC

我们前面主要去学习了蓝牙开发所具备的基础知识、常用的仿真手法,那么如何去分析我们抓到的蓝牙通信包呢?不去学习蓝牙通信协议,只能把抓到的Log信息,交给能看懂的人,你也最终只能当一个测试工程师了。

关键字: 基础知识 仿真手法 蓝牙通信

Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向...

关键字: Java C++

(全球TMT2022年4月26日讯)The Open Group,厂商中立的技术联盟,宣布正式发布TOGAF®标准第10版,这标志着这一世界上使用最广泛的企业架构框架的开发又步入一个重要里程碑。由The Open Gr...

关键字: GROUP PEN 模块化结构 基础知识

低压差 (LDO) 稳压器的本质是通过将多余的功率转化为热量来调节电压,使该集成电路成为低功率或小 V IN至 V OUT差分应用的理想解决方案。考虑到这一点,选择合适的 LDO 和合适的封装对于最大限度地提高应用程序的...

关键字: LDO 基础知识 BSP 封装

哈喽,大家好,我是瓜哥,致力于分享互联网各领域干货。前几天,有人问瓜哥,学习编程语言有什么好的建议没?今天简单和大家分享几点学习编程的建议,希望可以帮助到大家。1.只要开始,就不要怕晚瓜哥经常看到这些问题,大四学编程还来...

关键字: 编程 代码 基础知识 数据结构

控制器局域网(CAN)非常适合包含CAN和ISO-11898:2003作为其物理层的许多高级工业协议。它的成本、性能和可升级性提供了系统设计的巨大灵活性。本应用报告介绍了CAN基础知识、工作原理以及基本CAN总线与TI ...

关键字: CAN 基础知识
关闭
关闭