今天小编分享 C++多态和数据抽象 ,建议大家收藏慢慢学习,同时希望对大家的C++学习有所帮助。
C++ 多态
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
下面的实例中,基类 Shape 被派生为两个类,如下所示:
实例
#include<iostream>using namespace std;classShape{
protected:
int width,height;
public:
Shape(int a=0,int b=0)
{
width=a;height=b;
}
int area(){
cout<<"Parent class area :"<<endl;
return0;
}};
classRectangle:publicShape{
public:
Rectangle(int a=0,int b=0):Shape(a,b){}
int area(){
cout<<"Rectangle class area :"<<endl;
return(width*height);
}};classTriangle:publicShape{
public:
Triangle(int a=0,int b=0):Shape(a,b){}
int area(){
cout<<"Triangle class area :"<<endl;
return(width*height/2);
}};
// 程序的主函数int main( ){
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存储矩形的地址
shape = &rec;
// 调用矩形的求面积函数
area
shape->area();
// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数
area
shape->area();
return 0;}
当上面的代码被编译和执行时,它会产生下列结果:
ParentclassareaParentclassarea
导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接- 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。
但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,如下所示:
classShape{
protected:
int width,height;
public:
Shape(int a=0,int b=0)
{
width=a;
height=b;
}virtual int area(){
cout<<"Parent class area :"<<endl;
return0;
}};
修改后,当编译和执行前面的实例代码时,它会产生以下结果:
RectangleclassareaTriangleclassarea
此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。
正如大家所看到的,每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,就可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
虚函数
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
纯虚函数
大家可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是大家在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
我们可以把基类中的虚函数 area() 改写如下:
classShape{
protected:
int width,height;
public:Shape(int a=0,int b=0)
{
width=a;
height=b;
}
// pure virtual function
virtual int area() = 0;};
= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
C++ 数据抽象
数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。
数据抽象是一种依赖于接口和实现分离的编程(设计)技术。
让我们举一个现实生活中的真实例子,比如一台电视机,可以打开和关闭、切换频道、调整音量、添加外部组件(如喇叭、录像机、DVD 播放器),但是我们不知道它的内部实现细节,也就是说,我们并不知道它是如何通过缆线接收信号,如何转换信号,并最终显示在屏幕上。
因此,我们可以说电视把它的内部实现和外部接口分离开了,我们无需知道它的内部实现原理,直接通过它的外部接口(比如电源按钮、遥控器、声量控制器)就可以操控电视。
现在,让我们言归正传,就 C++ 编程而言,C++ 类为数据抽象提供了可能。它们向外界提供了大量用于操作对象数据的公共方法,也就是说,外界实际上并不清楚类的内部实现。
例如,程序可以调用 sort() 函数,而不需要知道函数中排序数据所用到的算法。实际上,函数排序的底层实现会因库的版本不同而有所差异,只要接口不变,函数调用就可以照常工作。
在 C++ 中,我们使用类来定义我们自己的抽象数据类型(ADT)。可以使用类 iostream 的 cout 对象来输出数据到标准输出,如下所示:
实例
#include<iostream>using namespace std;intmain(){
cout<<"Hello C++"<<endl;
return0;}
在这里,暂时不需要理解 cout 是如何在用户的屏幕上显示文本。只需要知道公共接口即可,cout 的底层实现可以自由改变。
访问标签强制抽象
在 C++ 中,我们使用访问标签来定义类的抽象接口。一个类可以包含零个或多个访问标签:
使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的数据抽象视图是由它的公共成员来定义的。
使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。
访问标签出现的频率没有限制。每个访问标签指定了紧随其后的成员定义的访问级别。指定的访问级别会一直有效,直到遇到下一个访问标签或者遇到类主体的关闭右括号为止。
数据抽象的好处
数据抽象有两个重要的优势:
类的内部受到保护,不会因无意的用户级错误导致对象状态受损。
类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。
如果只在类的私有部分定义数据成员,编写该类的作者就可以随意更改数据。如果实现发生改变,则只需要检查类的代码,看看这个改变会导致哪些影响。
如果数据是公有的,则任何直接访问旧表示形式的数据成员的函数都可能受到影响。
数据抽象的实例
C++ 程序中,任何带有公有和私有成员的类都可以作为数据抽象的实例。请看下面的实例:
实例
#include<iostream>using namespace std;classAdder{
public:
// 构造函数
Adder(int i = 0) {
total = i;
}
// 对外的接口
void addNum(int number){
total += number; }
// 对外的接口
int getTotal(){
return total;
};
private:
// 对外隐藏的数据
int total;};int main( ){
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;}
当上面的代码被编译和执行时,它会产生下列结果:
Total60
上面的类把数字相加,并返回总和。公有成员 addNum 和 getTotal 是对外的接口,用户需要知道它们以便使用类。私有成员 total 是用户不需要了解的,但又是类能正常工作所必需的。
C++ 数据封装
所有的 C++ 程序都有以下两个基本要素:
程序语句(代码):这是程序中执行动作的部分,它们被称为函数。
程序数据:数据是程序的信息,会受到程序函数的影响。
封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装引申出了另一个重要的 OOP 概念,即数据隐藏。
数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
C++ 通过创建类来支持封装和数据隐藏(public、protected、private)。我们已经知道,类包含私有成员(private)、保护成员(protected)和公有成员(public)成员。默认情况下,在类中定义的所有项目都是私有的。例如:
classBox{
public:
doublegetVolume(void)
{
returnlength*breadth*height;
}
private:
double length;
// 长度
double breadth;
// 宽度
double height;
// 高度};
变量 length、breadth 和 height 都是私有的(private)。这意味着它们只能被 Box 类中的其他成员访问,而不能被程序中其他部分访问。这是实现封装的一种方式。
为了使类中的成员变成公有的(即,程序中的其他部分也能访问),必须在这些成员前使用 public 关键字进行声明。所有定义在 public 标识符后边的变量或函数可以被程序中所有其他的函数访问。
把一个类定义为另一个类的友元类,会暴露实现细节,从而降低了封装性。理想的做法是尽可能地对外隐藏每个类的实现细节。
数据封装的实例
C++ 程序中,任何带有公有和私有成员的类都可以作为数据封装和数据抽象的实例。请看下面的实例:
实例
#include<iostream>using namespace std;classAdder{
public:
// 构造函数
Adder(int i = 0)
{
total = i;
}
// 对外的接口
void addNum(int number){
total += number;
}
// 对外的接口
int getTotal(){
return total;
};
private:
// 对外隐藏的数据
int total;};int main( ){
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.get Total() <<endl;
return 0;}
当上面的代码被编译和执行时,它会产生下列结果:
Total 60
上面的类把数字相加,并返回总和。公有成员 add Num 和 get Total 是对外的接口,用户需要知道它们以便使用类。私有成员 total 是对外隐藏的,用户不需要了解它,但它又是类能正常工作所必需的。
好了,以上就是对 C++多态和数据抽象的分享。
如果你喜欢这篇文章的话,动动小指,加个关注哦~
如果你也想成为程序员,想要快速掌握编程,这里为你分享一个学习企鹅圈子!
里面有资深专业软件开发工程师,在线解答你的所有疑惑~C++入门“so easy”
编程学习书籍:
编程学习视频: