文章标题
来源:互联网 发布:小米床垫怎么样 知乎 编辑:程序博客网 时间:2024/06/05 07:36
C++类的继承与派生 ##1、继承与派生
一、继承的概念
1、继承与派生
类的继承,即一个新类从已有的类那里获得其已有特性。通过继承,一个新建子类从已有的父类那里获得父类的特性,或者说,从已有的类(父类)产生一个新的子类,称为类的派生。派生类继承了基类的所有数据成员和成员函数,并可以对成员作必要的增加或调整。
2、派生类的声明方式
class 派生类名:[继承方式] 基类名
{
派生类新增加的成员
} ;
例:先声明一个基类Base,在此基础上通过单继承建立一个派生类Derived
#include<stdlib.h>#include<string>#include<iostream>using namespace std;class Base{public: void Print(int a) { _pub=0; _pro=1; _pri=2; cout<<"_pub"<<_pub<<endl; cout<<"_pro"<<_pro<<endl; cout<<"_pri"<<_pri<<endl; } public: int _pub;protected: int _pro;private: int _pri;};//派生类class Derived:public Base{public: void Print() { _pub=0; _pro=1; //_pri=2; }public: int _pubD;protected: int _proD;private: int _priD;};int main(){ Derived d; cout<<sizeof(Derived)<<endl; system("pause"); return 0;}
3、派生类的构成
4、派生类成员的访问属性
*注:若继承方式省略,使用关键字class时默认的继承方式是private,使用struct时默认继承方式public.
—派生类的默认成员函数在继承关系里面,在派生类中如果没有显示定义6个成员函数(构造函数、拷贝构造函数、析构函数、赋值运算符重载、取地址操作符重载、const修饰的取地址操作符重载),编译系统则会默认合成这六个默认的成员函数
5、派生类的构造函数和析构函数
1)、继承关系中构造函数调用顺序
构造函数调用的代码:
#include<stdlib.h>#include<string>#include<iostream>using namespace std;//基类class Base{public: Base() { cout<<"Base::Base()"<<endl; } void Print(int a) { _pub=0; _pro=1; _pri=2; cout<<"_pub"<<_pub<<endl; cout<<"_pro"<<_pro<<endl; cout<<"_pri"<<_pri<<endl; } public: int _pub;protected: int _pro;private: int _pri;};//派生类class Derived:public Base{public: Derived() { cout<<"Derived::Derived()"<<endl; } void Print() { _pub=0; _pro=1; }public: int _pubD;protected: int _proD;private: int _priD;};int main(){ Derived d; d.Print(); cout<<sizeof(Derived)<<endl; system("pause"); return 0;}
构造函数,函数体执行顺序,基类在前,派生类在后
构造函数的调用顺序,如下图所示:
1、
2、
3、
4、
5、
6、
2)、继承关系中析构函数调用
在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作。基类的清理工作仍然由基类的析构函数负责。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。
6、继承体系中的作用域
1)、在继承体系中基类和派生类是俩个不同作用域
2)、子类和父类中有同名的成员或成员函数,子类成员将屏蔽父类对成员的直接访问—隐藏—重定义
*实际中在继承体系里最好不要定义同名的成员
#include<stdlib.h>#include<string>#include<iostream>using namespace std;class Base{public: Base() {} void Print() { _pub=0; _pro=1; _pri=2; cout<<"_pub"<<_pub<<endl; cout<<"_pro"<<_pro<<endl; cout<<"_pri"<<_pri<<endl; } public: int _pub;protected: int _pro;private: int _pri;};//派生类class Derived:public Base{public: Derived()//派生类构造函数--在函数体中只对派生类新增的数据成员初始化 {} void Print() { _pubD=4; _proD=5; cout<<"_pubD"<<_pubD<<endl; cout<<"_proD"<<_proD<<endl; }public: int _pubD;protected: int _proD;private: int _priD;};int main(){ Derived d; d.Print(); cout<<sizeof(Derived)<<endl; system("pause"); return 0;}
运行结果如图所示:
7、继承与转换—赋值兼容规则—public继承
1)、子类对象可以赋值给父类对象(切割、换片)—>赋值的部分是子类继承父类的部分
2)、父类对象不能赋值给子类对象—>程序会崩溃
3)、父类的指针/引用可以指向子类对象
4)、子类的指针/引用可以指向父类对象——-可以通过强制类型转换完成
程序运行代码:
#include<stdlib.h>#include<string>#include<iostream>using namespace std;class Base{public: Base() {} void Print() { _pub=0; _pro=1; _pri=2; cout<<"_pub"<<_pub<<endl; cout<<"_pro"<<_pro<<endl; cout<<"_pri"<<_pri<<endl; } public: int _pub;protected: int _pro;private: int _pri;};//派生类class Derived:public Base{public: Derived()//派生类构造函数--在函数体中只对派生类新增的数据成员初始化 {} void Show() { d1=6; }public: int d1; int d2;};void FunTest(){ Base b; //定义基类Base对象b Derived d1; //定义公用派生类Derived对象d1 Base &r=b; //定义基类Base对象的引用变量r,并用b对其初始化 r=d1; //用派生类对象d1对Base的引用变量r赋值 b=d1; //用派生类Derived对象d1对基类对象b赋值 d1.d2=7; //d1中包含派生类中增加的成员}void fun(Base &r) //形参是类Base的对象的引用变量{ cout<<r._pub<<endl; //输出该引用变量的数据成员_pri;}int main(){ Derived d; FunTest(); d.Print(); d.Show(); fun(d); cout<<sizeof(Derived)<<endl; system("pause"); return 0;}
程序运行结果和分析:
8、继承与静态成员
基类定义了static成员(可以被继承),则整个继承体系里只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。
二、单继承&多继承&菱形继承
1、单继承:一个子类只有一个直接父类的继承关系为单继承
2、多继承:一个子类有两个或以上直接父类时的继承关系 为多继承
如果已经声明了类B1和B2,则可以声明多重继承的派生类D
代码如下:
#include<stdlib.h>#include<string>#include<iostream>using namespace std;class B1{public: int _b1;};class B2{public: int _b2;};class D:public B1,public B2{public: int _d;}; int main() { cout<<sizeof(D)<<endl; D d; d._b1 =0; d._b2 =1; d._d =2; system("pause"); return 0; }
程序运行结果:
多重继承的先后:
子类对象赋值给父类对象:
3、菱形继承—派生类对象访问基类—程序代码
#include<stdlib.h>#include<string>#include<iostream>using namespace std;
class B{public: int _b;};class C1:public B{public: int _c1;};class C2:public B{public: int _c2;};class D:public C1,public C2{public: int _d;}; int main() { cout<<sizeof(D)<<endl; D d; d.C1::_b =0; d._c1 =1; d.C2::_b =2; d._c2 =3; d._d =4; system("pause"); return 0; }
程序运行结果:
菱形继承体系中子类对象包含多份父类对象,发生数据冗余&浪费空间的问题,派生类D的 大小中公有继承俩个B类,引发程序的二义性问题:分析结果如图所示:
4、虚拟继承—–虚基类—菱形虚拟继承
虚基类并不是在声明基类是声明的,而是在声明派生类是,指定继承方式时声明的。
*为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,
#include<stdlib.h>#include<string>#include<iostream>using namespace std; class B{public: int _b;}; class D:virtual public B { public: int _d; }; int main() { cout<<sizeof(D)<<endl; D d; d._b =0; d._d =1; system("pause"); return 0; }
程序运行结果:
结果分析:
在内存中查看d的地址,会发现它的内存地址指向的也是一个地址,因此在内存c2中查看这个地址,发现它是一个指向偏移量表格的指针,编译器合成的派生类构造函数将偏移量表格指向前4个字节
- 文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题 文章标题 文章标题 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- 文章标题
- HTML5中 audio标签的样式修改
- Postman用法简介-Http请求模拟工具
- Python3.6安装以及numpy库、matplotlib库的安装方法(Win7)
- 快速傅立叶变换(FFT)的一种推导
- 学生信息管理系统(jdbc操作MySQL)
- 文章标题
- linux下svn使用小结 创建 添加仓库 版本管理
- iBET Slot Games Rebate 1% Unlimited Bonus(iBET, iBET Promotion, Online Casino Malaysia, Online Casin
- 2.1实现将一组整数进行升序排列
- RESTful架构
- 自动更新ListView时遇到onScrollStateChanged不执行的问题
- C++实现一个日期类
- Android App优化之性能分析工具
- OpenSessionInViewFilter原理以及为什么要用OpenSessionInViewFilter