概述
文章目录
- 前言
- 一、类的继承与派生
- 1.概念
- 2.单继承
- (1)语法格式:
- (2)基类成员在不同派生类中的引用权限
- (3)派生类与基类同名成员的访问方式
- (4)赋值兼容规则
- (5)单继承的构造与析构
- 3.多继承
- (1)语法格式:
- (2)多继承派生类的构造和析构:
- (3)多继承中的二义性问题
- 二、多态性
- 1.运算符的重载
- 2.虚函数
- 3.虚函数的实现机制
- 4.纯虚函数及抽象类
前言
此博客主要讲述类的继承与派生的相关内容,视频请参考
MOOC第九章
提示:以下是本篇文章正文内容,下面案例可供参考
一、类的继承与派生
1.概念
继承——子类具有父类的性质,即在已存在的类上建立另一个新的类。(已存在的类叫做基类或父类,新建立的类叫做子类或派生类)
派生——子类拥有父类没有的性质。
派生类的功能
- 吸收基类成员
- 改造基类成员
- 添加新成员
2.单继承
派生类只有一个直接基类
(1)语法格式:
class 派生类名 : <继承方式> 基类名{…//新成员和修改的基类成员}
继承方式包括public,private,protected.
补充:保护成员特性:
- 能够被基类和派生类访问
- 和私有成员一样,内外不能访问
(2)基类成员在不同派生类中的引用权限
基类 | 公有成员 | 私有成员 | 保护成员 |
---|---|---|---|
公有派生类 | 公有成员 | 不可访问成员 | 保护成员 |
私有派生类 | 私有成员 | 不可访问成员 | 私有成员 |
保护派生类 | 保护成员 | 不可访问成员 | 保护成员 |
(3)派生类与基类同名成员的访问方式
即基类成员名与派生类成员名相同
同名访问规则:即如何区分该成员是基类还是派生类成员
在派生类中使用基类的同名成员:语法格式:基类名::成员
若使用派生类成员,则直接使用。派生类成员覆盖基类成员
对象使用基类的成员:对象名.基类名::成员名。
class Base
{
protected:
int v1;
public:
int v2;
Base(int a = 0,int b = 0) { v1 = a; v2 = b; }
};
class Devrid :public Base
{
int v2;
public:
int v3;
Devrid(int a = 0, int b = 0)
{
v2 = a; v3 = b;
}
void func() {
int sum1 = v1 + v2 + v3;//使用派生类成员v2
int sum2 = v1 + Base::v2 + v3;//使用基类成员v2
}
};
int main()
{
Devrid obj(5, 6);
obj.Base::v2 = 8;//基类成员的v2,可以访问
//obj.v2 = 7;//派生类成员的v2,不可访问
return 0;
}
(4)赋值兼容规则
提出:数据类型可以相互转化,但不同类型的类对象不可以相互转换,赋值兼容规则就是将子类对象转化为基类对象
内容:
在公有派生方式下:派生类对象可以作为基类对象类使用,具体方法如下:
- 派生类对象直接赋值给基类对象
- 派生类对象赋值给基类对象的引用
- 基类对象的指针指向派生类对象
只能将派生类对象赋值给基类对象
(5)单继承的构造与析构
单继承派生类的构造函数
语法格式:
派生类构造函数(参数表):基类构造函数(参数表),对象成员名1(参数表), … 对象成员名n(参数表),
{
… //初始化自定义数据成员
}
如果基类使用缺省的构造函数或不带参的构造函数,则可以在初始化列表中省略,如果没有对象成员,也可以省略。
例子:
class Cirle
{
//Point center;
float radios;
public:
Cirle(float x, float y, float r) :center(x, y)//给point对象初始化
{
radios = r;
}
};
class ColorCirle :public Cirle
{
int color;
public:
ColorCirle(float x, float y, float r, int color) :Cirle(x, y, r)//先给基类对象初始化
{
this->color = color;
}
};
构造函数的调用顺序:
- 先调用基类构造函数
- 再调用对象成员所属类的构造函数
- 最后调用派生类构造函数
析构函数的调用顺序:
- 先调用派生类的析构函数
- 再调用对象成员所属类的析构函数
- 最后调用派生类析构函数
3.多继承
派生类只有多个直接基类
(1)语法格式:
class 派生类名 : <继承方式1> 基类名1,
<继承方式2> 基类名2,
<继承方式3> 基类名3,
{…//新成员和修改的基类成员}
继承方式包括public,private,protected.
(2)多继承派生类的构造和析构:
多继承派生类的构造函数
语法格式:
派生类构造函数(参数表):基类名1(参数表1),
基类名2(参数表2),
对象成员名1(参数表), … 对象成员名n(参数表),
{
… //派生类新添加的成员
}
派生类构造函数与析构函数执行顺序与单继承一致
基类对象的调用顺序与声明继承有关,对象成员的调用顺序按照类中的调用顺序。
(3)多继承中的二义性问题
可能出现的情况有:
-
访问不同基类的具有相同名字的成员时可能出现二义性
解决办法是用类名对成员加以限定,如c1.A::f() -
访问共同基类的成员可能出现二义性
解决办法是使用虚基类
class A
{
public:
int a;
void g(){}
};
class B1 :virtual public A
{
int b1;
};
class B2 :virtual public A
{
int b2;
};
class C :public B1, public B2
{
int c;
public:
int f(){}
};
int main()
{
C obj;
obj.a = 8;//通过B1->A来的,因为先调用B1的基类构造函数,而调用B2的基类构造函数时,发现这个基类是虚基类,因此直接引用
obj.g();
return 0;
}
虚基类调用构造函数的次序
- 虚基类调用构造函数在非虚基类之前
- 在同一层次包含多个虚基类,按照说明次序调用
- 若虚基类是非虚基类的派生,则先调用基类构造函数,后调用派生类构造函数
二、多态性
1.运算符的重载
多态性的分类:
-
编译时的多态:
- 函数重载
- 运算符重载 运行时的多态
- 虚函数
运算符重载:重新定义运算符作用在类类型上的含义
例子:复数的运算符重载
#include<iostream>
using namespace std;
class Complex
{
double re, im;
public:
Complex(double i=0.0,double r=0.0):re(r),im(i){}
friend Complex operator+(Complex c1, Complex c2);
friend ostream& operator<<(ostream& out, Complex& obj);
};
Complex operator+(Complex c1, Complex c2) {
Complex t;
t.re = c1.re + c2.re;
t.im = c1.im + c2.im;
return t;
}
ostream& operator<<(ostream& out, Complex& obj)
{
out << obj.re << "+" << obj.im << "i";
return out;
}
int main()
{
Complex c1(1, 2), c2(3, 4);
Complex c3 = c1 + c2;
cout << c3;
return 0;
}
2.虚函数
在该函数前加上virtual关键字,该函数即为虚函数
一般情况下,派生类拥有从基类继承的成员,因此相对于已经定义的基类对象,如果将此对象重新定义为派生类对象,派生类从基类继承的成员对这个对象是不可见的,也就是说不能使用。
为达到以上目的,应该使用虚函数。
虚函数的特性:可以在一个或多个派生类中被重新定义,但要求在重定义时虚函数的原型(包括返回值类型,函数名,参数列表)必须完全相同。
基类中的函数具有虚特性的条件:
- 在基类中用virtual将函数说明为虚函数
- 在公有派生类中原型一致的重载该函数
- 定义基类引用或指针,使其引用或指向派生类对象。当通过该引用或指针调用虚函数时,该函数将体现虚特性来
多态如何实现
基类必须指出希望被派生类重定义的那些函数,定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的不能定义为虚函数
3.虚函数的实现机制
实现机制是通过函数指针来实现的
虚函数表和虚指针:
- 在编译时,为每个有虚函数的类建立一张虚函数表VTABLE,表中存放的是每一个虚函数的指针;同时用一个虚指针VRTR指向这张表的入口
- 访问某个虚函数时,不是直接找到那个函数的地址,而是通过VRTR间接查到它的地址。
VRTR由构造函数初始化
对虚函数的要求:
- 虚函数必须是类的非静态成员
- 不能将虚函数说明为全局函数
- 不能将虚函数说明为静态成员函数
- 不能将虚函数说明为友元函数
期望将析构函数定义为虚函数,这样容易处理基类和派生类的空间,而构造函数不能定义为虚函数
4.纯虚函数及抽象类
基类中的公共接口只需要有说明而不需要有实现,即为纯虚函数。具体的实现由派生类定义。
语法形式:
virtual 函数类型 函数名(参数列表)=0
试例:
class Shap//抽象类
{
virtual float Perimeter() = 0;//纯虚函数定义
virtual float Area() = 0;
};
相关概念:
- 将一个函数说明为纯虚函数,就要求任何派生类都定义自己的实现。
- 拥有纯虚函数的类被称为抽象类。抽象类不能被实例化,只能作为基类被使用。
- 抽象类的派生类需要实现纯虚函数,若有一个没实现,则该类也为抽象类。
- 当抽象类的所有函数成员都是纯虚函数时,这个类被称为接口类。
最后
以上就是害怕月饼为你收集整理的类的继承与多态前言一、类的继承与派生二、多态性的全部内容,希望文章能够帮你解决类的继承与多态前言一、类的继承与派生二、多态性所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复