-
2019-10-20 21:21:34
派生类的声明:
class 派生类名:继承方式 基类名1, 继承方式 基类名2,...,继承方式 基类名n { 派生类成员声明; };
- 一个派生类可以同时有多个基类,这种情况称为多重继承。派生类只有一个基类,称为单继承。慎用多继承!
- 继承方式规定了如何访问基类继承的成员。继承方式有public, private, protected。如果不显示给出继承方式,默认为private继承。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
- 派生类继承基类中除构造和析构函数以外的所有成员。
- 派生类组成:
吸收基类成员(除构造析构函数以外的所有成员);
改造基类成员(根据继承方式调整基类成员的访问,函数在子类中的覆盖,以及虚函数在子类中的覆盖);
添加新的成员; - 派生类中由基类继承而来的成员的初始化工作还是由基类的构造函数完成,然后派生类中新增的成员在派生类的构造函数中初始化。
- 派生类构造函数的语法:
派生类名::派生类名(参数总表):基类1构造函数(参数表1),基类2构造函数(参数名2)....基类n构造函数(参数名n),成员属性1构造函数(参数表1),成员属性2构造函数(参数表2)....成员属性n构造函数(参数表n) { 派生类新增成员的初始化语句; } 注:构造函数的初始化顺序并不以上面的顺序进行,而是根据声明的顺序初始化。
- 派生类构造函数执行的次序:
调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左到右);
调用内嵌成员对象的构造函数,调用顺序按照它们在类中声明的顺序;
派生类的构造函数体中的内容。 - 派生类的析构函数的功能是在该对象消亡之前进行一些必要的清理工作,析构函数没有类型,也没有参数。析构函数的执行顺序与构造函数相反:先是派生类的析构函数,然后是内嵌成员对象的析构函数(类中声明顺序的反序),最后是基类析构函数(从右到左)。
#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
通过上述示例,可以理解构造函数和析构函数的调用顺序。
本文由博客一文多发平台 OpenWrite 发布!
更多相关内容 -
C++ 类的继承与派生实例详解
2021-01-01 10:10:12C++ 类的继承与派生实例详解 继承性是面向对象程序设计最重要的特性之一,使软件有了可重用性,C++提供的类的继承机制。 继承与派生的概念 一个新类从已有的类那里获得已有的特性,这种现象称为类的继承。同样也可以... -
继承与派生总结
2018-06-19 18:07:34c++继承与派生总结 面向对象的程序设计中提供了类的继承机制,允许程序员在保持原有类特性的基础上,进行更具体、更详细的类的定义。以原有的类为基础产生新的类,我们就说新类继承了原有类的特征,也就是说从原有类... -
类的继承与派生
2020-06-03 16:11:48继承与派生 1、继承与派生的定义 继承:保持原有类的特性而构建新类的过程 派生:在已有类的基础上新增自己的特性而产生新类的过程。 (其中,被继承的已有类称为基类,派生出来的新类称为派生类) 2、继承与派生的...继承与派生
1、继承与派生的定义
继承:保持原有类的特性而构建新类的过程
派生:在已有类的基础上新增自己的特性而产生新类的过程。
(其中,被继承的已有类称为基类,派生出来的新类称为派生类)2、继承与派生的目的
继承:实现代码重用
派生:当新的问题出现时,原有程序无法解决或不能完全解决时,需要对原有程序进行改造3、派生类的声明
class 派生类名:继承方式 基类名
{
……
}4、三种继承方式
A.公有继承
B.私有继承
C.保护继承
(不同继承方式的区别主要体现在:
——派生类成员对基类成员的访问权限
——派生类对象对基类成员的访问权限)A.公有继承(public)
&&基类的public和protected成员的访问属性在派生类中保持不变,但基类中的private成员不可直接访问
&&派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
&&通过派生类的对象只能访问基类的public成员//公有继承实例 //派生类访问公有继承的基类成员 #include<iostream> #include<string> using namespace std; class Student { private: int num; string name; string sex; public: void get_value()//声明一个输入函数 { cin>>num>>name>>sex; } void display()//声明一个输出函数 { cout<<"num: "<<num<<endl; cout<<"name: "<<name<<endl; cout<<"sex: "<<sex<<endl; } }; class Student1:public Student//公有继承 { private: int age; string addr; public: void get_value_1()//声明一个输入函数 { get_value();//通过基类的公有成员函数get_value()访问基类的私有成员 cin>>age>>addr; } void display_1()//声明一个输出函数 { display();//通过基类的公有成员函数display()访问基类的私有成员 cout<<"age:"<<age<<endl; cout<<"address:"<<addr<<endl; } }; /*由于基类的私有成员对派生类说是不能访问的,所以派生类的 成员函数display_1不能直接访问基类的私有成员。只能通过基类 的公有成员函数访问基类的私有成员。*/ int main() { Student1 stud1; stud1.get_value_1(); stud1.display_1(); return 0; }
B.私有继承(private)
&&基类的public和protected成员都已private身份出现在派生类中(降级),但基类中的private成员不可直接访问
&&派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;在派生类的外部,派生类的对象无法访问基类的全部成员。
&&通过派生类的对象不能直接访问基类的任何成员(全部基类成员在派生类中都成为了私有成员,无法进一步派生。私有继承方式一般很少使用)//私有继承实例 //与上述公有继承相似,只需做如下变化,其他相同部分省略 class Student1:private Student//私有继承
C.保护继承(protexted)
&&基类的public和protected成员都已protected身份出现在派生类中(降级),但是基类的private成员不可直接访问(但保护继承可以进一步派生,而私有继承则不可以)
&&派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
&&通过派生类的对象不能直接访问基类的任何成员5、类型兼容规则
A派生类的指针可以隐含转换为基类的指针.
B派生类的对象可以隐含转换为基类对象
C派生类对象可以初始化基类的引用#include <iostream> using namespace std; class B0 // 基类B0声明 { public: // 公有成员函数 void display(){cout<<"B0::display()"<<endl;} }; class B1: public B0 { public: void display(){cout<<"B1::display()"<<endl; } }; class D1: public B1 { public: void display(){cout<<"D1::display()"<<endl;} }; void fun(B0 *ptr) { ptr->display(); //"对象指针->成员名" } int main() //主函数 { B0 b0; //声明B0类对象 B1 b1; //声明B1类对象 D1 d1; //声明D1类对象 B0 *p; //声明B0类指针 p=&b0; //B0类指针指向B0类对象 fun(p); p=&b1; //B0类指针指向B1类对象 fun(p); p=&d1; //B0类指针指向D1类对象 fun(p); return 0; } 运行结果为:B0::display() B0::display() B0::display()
6、派生类的构造函数
声明方法:
派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名(参数表)
{
本类成员初始化赋值语句;
};
Tips:
&&基类的构造函数不被继承,派生类中需要声明自己的构造函数,派生类的构造函数需要给基类的构造函数传递参数
&&定义构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化可自动调用基类构造函数完成//在建立一个对象时,执行构造函数的顺序是:①派生类构造函数先调用基类构造函数;②再执行派生类构造函数本身(即派生类构造函数的函数体),用派生类构造函数的形参做基类构造函数的实参 #include<iostream> #include <string> using namespace std; class Student //声明基类 { public: Student(int n,string nam,char s) //基类构造函数 { num=n; name=nam; sex=s; } ~Student( ) { } //基类析构函数(内容为空) protected: int num; string name; char sex ; }; class Student1: public Student // 声明公用派生类 { public: Student1(int n,string nam,char s,int a,char ad[ ] ): Student (n,nam,s) // 派生类构造函数 { age=a; // 对派生类新增的数据成员初始化 addr=ad; } void show( ) { cout<<"num: "<<num<<endl; cout<<"name: "<<name<<endl; cout<<"sex: "<<sex<<endl; cout<<"age: "<<age<<endl; cout<<"address: "<<addr<<endl; } private: // 派生类的私有部分 int age; string addr; }; int main( ) { Student1 stud1(10010,"Wang-li",'f',19,"115 Beijing Road,Shanghai"); Student1 stud2(10011,"Zhang-fun",'m',21,"213 Shanghai Road,Beijing"); stud1.show( ); // 输出第一个学生的数据 stud2.show( ); // 输出第二个学生的数据 return 0; }
7、同名隐藏规则
&&若未强行指明,则通过派生类对象使用的是派生类的同名成员。(基类中的同名成员函数会被隐藏)
&&若要通过派生类对象访问基类中被隐藏的同名成员,应该使用基类名限定8、二义性的问题
产生:当派生类从多个基类派生,而这些基类有从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性。
解决方法:
A.用类名来限定 eg.c1.A::f()或c1.b::f()
B.同名隐藏,即在派生类中声明一个同名成员函数
C.虚基类(最标准的方法)
( 虚基类的声明
class 派生类名:virtual 继承方式 基类名)
在第一级继承时,就要将共同基类设置为虚基类
-
C++继承与派生类习题.pdf
2020-06-26 12:39:37第九章继承与派生类 92 典型例题分析与解答 例题 1下列对派生类的...答案D 分析一个派生类可以作为另一个派生类的基类无论是单继承还是多继承派生类至少有 一个基类派生类的成员除了它自己的成员外还包含了它的基类成员 -
C++继承与派生
2022-04-04 14:45:371.继承与派生:保持已有类的特征构造新类的过程为继承,在已有类的基础上新增特性而产生新类的过程称为派生 2.继承目的:实现代码重用;派生目的:实现源程序的改造 3.声明:class 派生类:继承方式 基类 4.派生类:...一.类的继承与派生:
1.继承与派生:保持已有类的特征构造新类的过程为继承,在已有类的基础上新增特性而产生新类的过程称为派生
2.继承目的:实现代码重用;派生目的:实现源程序的改造
3.声明:class 派生类:继承方式 基类
4.派生类:
吸收基类成员:派生类包含了基类中除了构造析构函数以外的所有成员,构造析构函数不会被继承
改造基类成员:虚函数重写(实际是虚函数表的覆盖)以及同名函数
添加新的成员:加入新的构造析构函数以及其他成员二.类成员的访问:public--->protected--->private注意基类中私有成员不能被访问
基类中的私有成员派生类无法访问
继承方式为public时权限不变,但是私有成员也会被继承?
继承方式为private何protected(体现类的继承特征)时,权限与继承方式一致
三.类型兼容规则:一个公有派生类的对象在使用上可以被当成基类的对象,反之不行
1.派生类的对象可以赋值给基类对象
2.派生类的对象可以初始化基类的引用
3.指向基类的指针可以指向派生类
通过基类的对象名与指针只能使用从基类继承的成员#include<iostream> #include <string> using namespace std; class Base { public: void showBase() { cout << "base成员函数的调用" << endl; } }; class Son :public Base { public: void showSon() { cout << "Son成员函数的调用" << endl; } }; int main() { Son s1; Base b1 = s1;//派生类对象赋值给基类对象 Base& b2 = s1;//派生类对象初始化基类的引用 Base* b3 = &s1;//派生类地址初始化基类指针/基类指针指向派生类 b1.showBase();//通过基类的对象名和指针只能调用基类成员 b2.showBase(); b3->showBase(); return 0; } //运行结果: //base成员函数的调用 //base成员函数的调用 //base成员函数的调用
四.单继承与多继承
1.单继承:一个基类一个派生类
2.多继承:多基类一派生 class 派生类:继承方式1 基类1, 继承方式2 基类2
3.多重派生:一个基类多个派生类
4.多层派生:派生类为基类继续派生
五.派生类的构造与析构1.基类的构造析构函数不会被继承,派生类需要写自己的构造析构函数
2.派生类的构造函数需要给基类构造函数传递参数,注意基类成员用基类名在初始化列表进行初始化!!!#include<iostream> #include <string> using namespace std; class Base { public: int m_A; Base(int a):m_A(a){}//如果有默认构造,派生类构造时可以不传参 void showBase() { cout << "m_A=" << m_A << endl; } }; class Son :public Base { public: int m_B; Son(int a,int b):Base(a),m_B(b){}//注意基类用基类名在初始化列表进行初始化,多个基类类推 void showSon() { cout << "m_B="<<m_B << endl; } }; int main() { Son s(10, 20); s.showBase(); s.showSon(); return 0; } //运行结果: //m_A = 10 //m_B = 20
六.类成员的标识与访问1.当派生类与基类成员同名时,优先调用派生类成员,基类同名成员被屏蔽(包括重载)
2.通过作用域来访问#include<iostream> #include <string> using namespace std; class Base { public: int m_A; Base(int a):m_A(a){} void show() { cout << "基类m_A=" << m_A << endl; } void show(int a) { cout << "基类重载" << endl; } }; class Son :public Base { public: int m_A; Son(int a1,int a2):Base(a1),m_A(a2){} void show() { cout << "派生类m_B="<<m_A << endl; } }; int main() { Son s(10, 20); s.show();//默认派生类,屏蔽了基类同名成员,包括重载 s.Base::show();//作用域访问基类 s.Base::show(10);//作用域访问基类重载类型 return 0; } //运行结果: //派生类m_B = 20 //基类m_A = 10 //基类重载
3.菱形继承引发的二义性问题:B1,B2继承A,C继承B1和B2,解决方法:同名屏蔽或虚函数
A类称为虚基类,在继承前加virtual,vbptr虚基类指针virtual base pointer指向虚基数表
#include <iostream> using namespace std; class BaseA { public: int m_A; BaseA(int a):m_A(a){} }; class BaseB1 :virtual public BaseA//虚继承 { public: int m_B1; BaseB1(int a,int b1):BaseA(a),m_B1(b1){} }; class BaseB2 :virtual public BaseA//虚继承 { public: int m_B2; BaseB2(int a,int b2):BaseA(a),m_B2(b2){} }; class SonC :public BaseB1, public BaseB2 { public: int m_C; SonC(int a,int b1,int b2,int c):BaseA(a),BaseB1(a,b1),BaseB2(a,b2),m_C(c){} }; int main() { SonC c(30,20,20,30);//B1,B2也可以初始化虚基类,但是C直接初始化优先级更高,调用了一次 cout << c.m_A << endl; return 0; } //运行结果:30 /* class BaseB1 size(12): +--- 0 | {vbptr} 4 | m_B1 +--- +--- (virtual base BaseA) 8 | m_A +--- BaseB1::$vbtable@: 0 | 0 1 | 8 (BaseB1d(BaseB1+0)BaseA) vbi: class offset o.vbptr o.vbte fVtorDisp BaseA 8 0 4 0 */ /* class SonC size(24): +--- 0 | +--- (base class BaseB1) 0 | | {vbptr} 4 | | m_B1 | +--- 8 | +--- (base class BaseB2) 8 | | {vbptr} 12 | | m_B2 | +--- 16 | m_C +--- +--- (virtual base BaseA) 20 | m_A +--- SonC::$vbtable@BaseB1@: 0 | 0 1 | 20 (SonCd(BaseB1+0)BaseA) SonC::$vbtable@BaseB2@: 0 | 0 1 | 12 (SonCd(BaseB2+0)BaseA) vbi: class offset o.vbptr o.vbte fVtorDisp BaseA 20 0 4 0 */
建立对象所指定的类称为最(远)派生类
虚基类的成员由最派生类调用虚基类的构造函数进行初始化,在初始化列表调用构造函数初始化。虚函数与纯虚函数在多态进行总结
-
实验四、类的继承与派生 类的继承与派生
2010-11-29 22:03:31类的继承与派生 注释详细 虚拟继承,派生对象访问 操作多多 -
实验四 C++中的继承与派生.doc
2020-10-05 09:50:21实验四 C++中的继承与派生 一实验目的 理解C++中继承与派生的概念 掌握C++中各种不同继承方式的访问属性差别 掌握单继承与多继承的实现方法 掌握派生类构造函数与析构函数的实现及调用顺序 掌握虚基类的使用方法 二... -
C++ 课程作业 继承与派生(Shape Circle Rectangle类的设计,继承与派生)
2020-04-18 17:17:08Circle类从shape类继承,并派生以下成员 1)私有成员r 2)公有getter和setter 3)计算面积函数getArea(),返回计算面积; 4)构造与析构函数 Rectangle类从shape类继承,并派生以下成员 1)私有成员h,w 2)公有getter... -
类的继承与派生实验
2013-12-03 13:16:191:掌握单继承和多继承下派生类的定义方法,理解基类成员在不同的继承方式下不同的访问属性。 2:正确定义派生类的构造函数与析构函数,理解定义一个派生类对象时构造函数、析构函数的调用次序。 3:理解同名冲突的... -
C++派生类与继承(超详细)
2020-08-15 16:57:30二、派生类的构造函数与析构函数 2.1、派生类构造函数和析构函数的执行顺序 通常情况下,当创建派生类对象时, 首先执行基类的构造函数, 随后再执行派生类的构造函数; 当撤消派生类对象时, 则先执行派生类的析构函数, ...一、派生类的概念
1.1、为什么要使用继承
1.2、派生类的说明
1.3、保护成员的作用(protected)
二、派生类的构造函数与析构函数
2.1、派生类构造函数和析构函数的执行顺序
通常情况下,当创建派生类对象时, 首先执行基类的构造函数, 随后再执行派生类的构造函数; 当撤消派生类对象时, 则先执行派生类的析构函数, 随后再执行基类的析构函数。
下列程序的运行结果,反映了基类和派生类的构造函数及析构函数的执行顺序。2.2、派生类构造函数和析构函数的构造规则
当基类的构造函数没有参数,或没有显式定义构造函数时, 派生类可以不向基类传递参数,甚至可以不定义构造函数。派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。
在 C + + 中,派生类构造函数的一般格式为:派生类构造函数名(参数表) :基类构造函数名( 参数表) { / / … }
其中基类构造函数的参数,通常来源于派生类构造函数的参数表, 也可以用常数值。
下面的程序说明如何传递一个参数给派生类的构造函数和传递一个参数给基类的构造函数。说明:
- 当基类构造函数不带参数时, 派生类不一定需要定义构造函数, 然而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数,
甚至所定义的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。 - 若基类使用缺省构造函数或不带参数的构造函数, 则在派生类中定义构造函数时可略去“∶基类构造函数名(参数表)”;此时若派生类也不需要构造函数, 则可不定义构造函数。
- 如果派生类的基类也是一个派生类, 则每个派生类只需负责其直接基类的构造,依次上溯。 由于析构函数是不带参数的,在派生类中是否要定义析构函数与它所属的基类无关,故基类的析构函数不会因为派生类没有析构函数而得不到执行, 它们各 自是独立的。
三、多重继承
3.1、多重继承的声明
一般形式如下:
class 派生类名: 派生方式 1 基类名 1, …,派生方式 n 基类名 n { // 派生类新增的数据成员和成员函数 } ;
冒号后面的部分称基类表,各基类之间用逗号分隔, 其中“派生方式 i”( i = 1, 2, …, n )规定了派生类从基类中按什么方式继承: private 或 public,缺省的派生方式是 private
class z: public x, y { // 类 z 公有继承了类 x,私有继承了类 y // … }; class z: x, public y { // 类 z 私有继承了类 x,公有继承了类 y // … }; class z: public x, public y { // 类 z 公有继承了类 x 和类 y // … };
如下:对基类成员的访问必须是无二义的, 例如下列程序段对基类成员的访问是二义的,必须想法消除二义性。
class X { public: int f(); }; class Y { public: int f(); int g(); }; class Z∶public X, public Y { public: int g(); int h(); };
假如定义类 Z 的对象 obj: Z obj;则以下对函数 f( )的访问是二义的:
-
obj .f( ) ;
/ / 二义性错误,不知调用的是类 X 的 f( ) ,还是类 Y 的 f( )
使用成员名限定可以消除二义性,例如: -
obj .X∷f( ) ;
/ / 调用类 X 的 f( ) -
obj .Y∷f( ) ;
/ / 调用类 Y 的 f( )
3.2、多重继承的构造函数与析构函数
多重继承构造函数的定义形式与单继承构造函数的定义形式相似, 只是 n 个基类的构造函数之间用“,”分隔。多重继承构造函数定义的一般形式如下:
派生类构造函数名(参数表) :基类 1 构造函数名 ( 参数表), 基类 2 构造函数名 (参数表), …,基类 n 构造函数名(参数表) { // … }
例如,由一个硬件类 Hard 和一个软件类 Soft ,它们共同派生出系统类 System, 声明如下:
class Hard { protected: char bodyname[20]; public: Hard(char * bdnm ); // 基类 Hard 的构造函数 // … }; class Soft { protected: char os[10]; char Lang[15]; public: Soft( char * o, char * lg);// 基类 Soft 的构造函数 // … } ; class System: public Hard, public Soft { private: char owner[10] ; public: System( char * ow, char * bn, char * o, char * lg) // 派生类 System 的构造函数 ∶Hard( bn), Soft(o, lg); // 缀上了基类 Hard 和 Soft 的构造函数 // … };
注意:在定义派生类 System 的构造函数时,缀上了 Hard 和 Soft 的构造函数。
再如,现有一个窗口类 window 和一个滚动条类 scrollbar, 它们可以共同派生出一个带有滚动条的窗口,声明如下:class window { // 定义窗口类 window // … public: window(int top, int left, int bottom, int right); ~window(); // … } ; class scrollbar { // 定义滚动条类 scrollbar // … public: scrollbar(int top, int left, int bottom, int right); ~scrollbar(); // … }; class scrollbarwind∶window,scrollbar { / / 定义派生类 / / … public: scrollbarwind(int top, int left, int bottom, int right); ~scrollbarwind(); // … }; scrollbarwind∷scrollbarwind(int top, int left, int bottom, int right)∶window( top, left,bot tom, right),scrollbar(top, right - 20, bottom, right) { // … }
在这个例子中, 定义派生类 scrollbarwind 的构造函数时, 也缀上了基类 window 和scrollbar 的构造函数。
下面我们再看一个程序,其中类 X 和类 Y 是基类, 类 Z 是类 X 和类 Y 共同派生出来的,请注意类 Z 的构造函数的定义方法。
#include<iostream> using namespace std; class X { int a; public: X(int sa ) // 基类 X 的构造函数 { a = sa; } int getX() { return a ; } }; class Y { int b; public: Y(int sb) // 基类 Y 的构造函数 { b = sb; } int getY() { return b; } } ; class Z: public X, private Y { // 类 Z 为基类 X 和基类 Y 共同的派生类 int c; public: Z(int sa, int sb, int sc ) :X(sa), Y(sb) // 派生类 Z 的构造函数,缀上 { c = sc ; } // 基类 X 和 Y 的构造函数 int getZ() { return c; } int getY() { return Y::getY(); } }; int main() { Z obj( 2, 4, 6) ; int ma = obj.getX(); cout << "a = "<< ma << endl; int mb = obj .getY(); cout << "b = "<< mb << endl; int mc = obj .getZ(); cout << "c = "<< mc << endl; return 0 ; }
上述程序运行的结果如下:
a = 2
b = 4
c = 6
由于派生类 Z 是 X 公有派生出来的, 所以类 X 中的公有成员函数 getX( )在类 Z 中仍是公有的, 在 main ( ) 中可以直接引用, 把成员 a 的值赋给 main ( ) 中的变量 ma ,并显示在屏幕上。类 Z 是从类 Y 私有派生出来的, 所以类 Y 中的公有成员函数 getY( ) 在类 Z 中成为私有的,在 main( ) 中不能直接引用。为了能取出 b 的值,在 Z 中另外定义了一个公有成员函数 Z∷getY( ) ,它通过调用 Y∷getY( ) 取出 b 的值。主函数 main( )中的语句:int mb = obj .getY( ) ;调用的是派生类 Z 的成员函数 getY( ) , 而不是基类 Y 的成员函数 getY( )。由于类 Z 中的成员函数 getZ( ) 是公有成员,所以在 main( ) 中可以直接调用取出 c 的值。总结:
多重继承的构造函数的执行顺序与单继承构造函数的执行顺序相同, 也是遵循先执行基类的构造函数,再执行对象成员的构造函数, 最后执行派生类构造函数的原则。在多个基类之间, 则严格按照派生类声明时从左到右的顺序来排列先后。而析构函数的执行顺序则刚好与构造函数的执行顺序相反。3.3、虚基类
不难理解,如果在上例中类 base 只存在一个拷贝, 那么对 a的引用就不会产生二义性。在 C + + 中,如果想使这个公共的基类只产生一个拷贝,则可以将这个基类说明为虚基类。这就要求从类 base 派生新类时, 使用关键字 virtual 将类base 说明为虚基类。
#include <iostream> using namespace std; class base { protected: int a; public: base() { a = 5; } }; class base1:virtual public base { public: base1() { cout << "base1 a = "<< a << endl; } }; class base2:virtual public base { public: base2() { cout << "base2 a = "<< a << endl; } }; class derived:public base1, public base2 { public: derived() { cout << "derived a = "<< a << endl; } }; int main () { derived obj; return 0 ; }
在上述程序中,从类 base 派生出类 base1 和类 base2 时,使用了关键字 virtual ,把类base 声明为 base1 和 base2 的虚基类。这样, 从 base1 和base2 派生出的类 derived 只有一个基类 base, 从而可以消除二义性。
虚基类的初始化
虚基类的初始化与一般的多重继承的初始化在语法上是一样的,但构造函数的调用顺序不同。虚基类构造函数的调用顺序是这样规定的:- 若同一层次中包含多个虚基类, 这些虚基类的构造函数按对它们说明的先后次序调用。
- 若虚基类由非虚基类派生而来, 则仍然先调用基类构造函数, 再调用派生类的构造函数。
- 若同一层次中同时包含虚基类和非虚基类, 应先调用虚基类的构造函数, 再调用非虚基类的构造函数,最后调用派生类构造函数, 例如:
class X∶public Y, virtual public Z { // … }; X one;
定义类 X 的对象 one 时,将产生如下的调用次序:
Z( ) ;
Y( ) ;
X( ) ;#include<iostream> using namespace std; class base { int a ; public: base (int sa) { a = sa; cout << "Constructing base"<< endl; } }; class base1: virtual public base { int b; public: base1 (int sa, int sb) : base(sa) { b = sb; cout << "Constructing baes1"<< endl; } }; class base2: virtual public base { int c; public: base2 (int sa, int sc) : base (sa) { c = sc ; cout << "Constructing baes2"<< endl; } }; class derived: public base1, public base2 { int d; public: derived(int sa, int sb, int sc, int sd ) : base(sa ), base1 (sa,sb), base2(sa, sc ) { d = sd; cout << "Constructing derived"<< endl; } }; int main() { derived obj(2, 4, 6, 8 ) ; return 0 ; }
在上述程序中, base 是一个虚基类,它只有一个带参数的构造函数, 因此要求在派生类 base1、base2 和 derived 的构造函数的初始化表中,都必须带有对 base 构造函数的调用。
如果 base 不是虚基类,在派生类 derived 的构造函数的初始化表中调用 base 的构造函数是错误的,但是当 base 是虚基类且只有带参数的构造函数时, 就必须在类 derived 构造函数的初始化表中调用类 base 的构造函数。因此, 在 derived 构造函数的初始化表中,不仅含有对 base1 和 base2 构造函数的调用, 还有对虚基类 base 构造函数的调用。上述程序运行的结果为:
Constructing base
Constructing base1
Constructing base2
Constructing derived不难看出,上述程序中虚基类 base 的构造函数只执行了一次。显然, 当 derived 的构造函数调用了虚基类 base 的构造函数之后, 类 base1 和类 base2 对 base 构造函数的调用被忽略了。这也是初始化虚基类和初始化非虚基类不同的地方。
说明:
- 关键字 virtual 与派生方式关键字 ( public 或 private ) 的先后顺序无关紧要, 它只说明是“虚拟派生”。 例如以下两个虚拟派生的声明是等价的。
class derived: virtual public base{ // … } ; class derived: public virtual base{ // … } ;
- 一个基类在作为某些派生类虚基类的同时, 又作为另一些派生类的非虚基类, 这种情况是允许存在的,例如:
class B{ // … } ; class X∶virtual public B{ // … } ; class Y∶virtual public B{ // … } ; class Z∶public B{ // … } ; class AA∶public X, public Y , public Z{ // … } ;
此例中,派生类 AA 由类 X、类 Y 和类 Z 派生而来。AA 与它的间接基类 B 之间的对应关系是:类 B 既是 X、Y 继承路径上的一个虚基类, 也是 Z 继承路径上的一个非虚基类。
- 当基类构造函数不带参数时, 派生类不一定需要定义构造函数, 然而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数,
-
继承与派生
2014-04-15 13:12:32.掌握继承和派生的定义,派生类的定义方法。 (1)掌握继承的两种类型:单继承和多继承。 (2)掌握private,public,protected三种继承方式的特点 -
类的继承与派生谭浩强C++.ppt
2020-12-01 07:57:08类的继承与派生 ;基类和派生类 ;基类和派生类;基类和派生类;基类和派生类;基类和派生类;基类和派生类;基类和派生类;基类和派生类;基类和派生类;单继承 ;单继承;单继承;单继承;单继承;单继承;单继承;单继承;单继承;单... -
C++复习---类的继承与派生.ppt
2020-05-09 06:44:38C++复习3 ----类的继承与派生 为什么要继承 如果软件编写的工程规模很大重用已经测试过的类代码要比重新编写新代码节省开发时间 基类与派生类 从已经定义的类产生新类的过程称为派生在派生过程中已定义的类称为基类... -
C++继承与派生类实验报告
2008-12-25 21:42:42本资源是C++继承与派生类实验报告,欢迎大家下载阿! -
C++ 课程作业 继承与派生 (motorcycle类设计(虚基类))
2020-04-18 17:16:16从bicycle和motorcar派生出motorcycle,观察虚基类对继承的影响。 定义一个motorcycle的对象,分别调用run()和stop(),观察构造/析构函数的调用情况。 注意:构造函数和析构函数中均为cout语句,说明哪个构造/析构... -
C/C++ 类的继承与派生
2019-05-25 19:47:15本节课我们学习了类的继承和派生,单继承、多重继承、多继承与虚基类等知识。 这道题实际上就是对知识的直接考察,故本次笔记侧重于对知识的罗列。 2精讲 参考文章:C++ 类的继承与派生 C++中的继... -
C# - 类的继承与派生
2019-09-22 09:46:55派生类构造函数初始化基类 继承后成员的可访问性 多继承(接口实现) new与重写 修饰符 多态 一:无参构造函数调用有参构造函数初始化 public A():this("AI-1") { } public A(string data) { name = data; ... -
C++程序设计【五】之 类的继承与派生
2021-02-25 10:43:24文章目录第五章:类的继承与类的派生一、类的继承与类的派生1.继承的概念2.派生类的定义与大小a.派生类的定义b.类的大小c.继承关系的特殊性3.继承关系的特殊性4.有继承关系的类之间的访问5.protected访问范围说明符6... -
面对对象与程序设计中的继承与派生
2011-07-17 12:15:04关于C++面对对象程序设计的知识中的继承和派生知识课件。对于一些刚刚学习c++的同学接触面对对象时很有帮助。 -
继承和派生复习
2021-01-20 22:06:48继承和派生练习关于保护继承下列说法错误的是保护继承的特点是基类的所有公用成员和保护成员都成为派生类的保护成员派生类对象不能访问基类中的任何成员派生类的对象可以访问基类的公有成员保护继承的派生类和私有继 -
c++课件 派生类与继承
2012-05-08 15:51:57c++课件 派生类与继承 -
c++类的继承与派生--实验报告.doc
2021-10-12 16:43:48c++类的继承与派生--实验报告.doc -
继承与派生类PPT学习教案.pptx
2021-10-03 13:16:58继承与派生类PPT学习教案.pptx -
c++程序设计 继承与派生实验(二)
2012-04-25 21:52:24c++程序设计 继承与派生实验(二) 1. 进一步掌握类的派生与继承的概念、应用方法 2. 掌握继承方式对成员访问权限的影响 3. 掌握虚基类的用法 -
C++实验三 派生类与继承.doc
2020-04-02 22:36:50实验三 派生类与继承 班级123班 姓名朱广金 学号122536 一实验目的 1学习类的继承能够定义和使用类的继承关系 2学习派生类的声明与定义方法 3掌握类的定义和对象的声明 4熟悉公有派生和私有派生的访问特性 5掌握派生... -
类的继承和派生
2019-09-03 23:21:11一、类的继承与派生: 1、类的继承是新的类从已有类那里得到已有的特性 2、 从已有类产生新类的过程就是类的派生; 3、类的继承与派生机制允许程序员在保持原有类特性的基础上,进行更具体,更详细的修改和...