C++ 继承、派生、多态

摘要:
现有类称为基类,也称为父类;新创建的类称为派生类,也称为子类。派生类中的友元函数只能访问派生类的私有成员,但不能访问基类的私有成员。复制构造函数仅用于从同一类或派生类复制到基类,而不用于从基类复制到派生类。注意区域分类和功能的多态性。摘要派生类继承自基类,可以描述基类对象集合中一组更详细和特殊的对象。

继承就是在一个已存在的类的基础上建立一个新的类。

已存在的类称为基类,又称父类;新建立类称为派生类,又称为子类

继承允许我们依据另一个类来定义一个类,不需要重新编写一部分的数据成员和成员函数,达到了重用代码功能和提高执行效率的效果。

基类与派生类

一个类可以派生自多个类,从多个基类继承数据和函数。我们使用一个类派生列表来指定基类。

class derived-class: access-specifier base-class

其中,访问修饰符 access-specifierpublicprotectedprivate 其中的一个,如果未指定则默认 private

搬运一个例子

class Shape {
public:
    void setWidth(int w) {
        width = w;
    }
    void setHeight(int h) {
        height = h;
    }
protected:
    int width;
    int height;
};
class Rectangle: public Shape {
public:
    int getArea() {
        return (width * height);
    }
};
int main(void) {
    Rectangle Rect;
    Rect.setWidth(5);
    Rect.setHeight(7);
    cout << "Total area: " << Rect.getArea() << endl;
}

派生类继承了基类的属性,继承了基类的行为,可以添加额外的行为,也可以重新定义这些行为。

若基类定义的数据成员和派生类添加的数据成员同名,基类数据成员被覆盖。注意,即使函数名相同,参数不同,也无法调用基类函数。

在访问时,句柄的类型决定哪个类的成员被访问。使用对象名或引用访问同名成员,根据引用的类型决定,使用基类指针访问同名的成员,根据指针类型决定。

访问控制

派生类可以访问基类中所有的非私有成员。一个派生类继承了除去构造函数、析构函数、重载运算符、友元函数以外所有的基类方法.

不同继承类型的规则如下

  • 公有继承(public):
    • 基类的公有成员也是派生类的公有成员
    • 基类的保护成员也是派生类的保护成员
    • 基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。
  • 保护继承(protected):
    • 基类的公有保护成员将成为派生类的保护成员。
  • 私有继承(private):
    • 基类的公有保护成员将成为派生类的私有成员。

基类的友元函数可以访问基类中的私有成员,以及派生类从基类中继承的成员。派生类中的友元函数只能访问派生类的私有成员,而不能访问基类中的私有成员。

拷贝构造函数仅用于同类拷贝或派生类向基类拷贝,而不能用于基类向派生类拷贝。

虚拟与多态

虚函数与纯虚函数

定义虚函数是为了允许用基类的指针来调用子类的这个函数。

例如以下程序的输出结果为 B

class A {
public:
    virtual void foo() {
        cout<<"A"<<endl;
    }
};
class B:public A {
public:
    void foo() {
        cout<<"B"<<endl;
    }
};
int main() {
    A *a = new B();
    a->foo();
}

在这里,调用的是哪个函数不是在编译时刻被确定的,而是在运行时刻被确定的。换言之,在编译时,并不知道被调用的是基类的函数还是哪个派生类的函数。

定义一个函数为纯虚函数,代表函数没有被实现,只是为了实现一个接口。它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。定义方式为

virtual void func()=0;

多态

在面向对象语言中,接口的多种不同的实现方式即为多态。

类的多态允许将子类类型的指针赋值给父类类型的指针。赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。

从应用的角度说,在基类的函数前加上 virtual 关键字声明为虚函数后,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

所有虚类的对象里面会自动加上一个隐藏指针,指向一张属于类的表 vtable,里是所有 virtual 函数的地址。

带有虚函数的类

每一个类都有虚表,且虚表可以继承。

如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。

如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。

如果派生类有自己的虚函数,那么虚表中就会添加该项。

所谓类的多态性,即将基类的函数定义为虚函数,在派生类中重写,运行时将会根据对象的实际类型来调用相应的函数。

注意区分类的多态性与函数的多态性。函数的多态性指一个函数被定义成多个不同参数的函数,调用这个函数时,就会调用不同的同名函数。

总结

派生类继承自基础类,可以描述基类对象集合中一组更细化、特殊的对象。派生类继承了基类的属性,继承了基类的行为,可以添加额外的行为,也可以重新定义这些行为。

虚函数是为了允许用基类的指针来调用子类的这个函数。调用的是哪个函数不是在编译时刻被确定的,而是在运行时刻被确定的。

定义一个函数为纯虚函数,代表函数没有被实现,只是为了实现一个接口。它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。

类的多态允许将子类类型的指针赋值给父类类型的指针。赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。

免责声明:文章转载自《C++ 继承、派生、多态》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇android2.2 watchdog分析3、zabbix配置入门下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#调试器导航

本快速入门演示如何在 Visual Studio 调试会话中导航,以及如何在会话中查看和更改程序状态。 本 快速入门适用于不熟悉用 Visual Studio 进行调试的开发人员,以及要详细了解在 Visual Studio 调试会话中导航的开发人员。但其中不传授调试本身的技艺。示例代码中的方法仅为演示本主题中所述的调试过程。这些方法并未采用应用程序或函...

Linux 系统编程 学习:3-进程间通信1:Unix IPC(2)信号

背景 上一讲我们介绍了Unix IPC中的2种管道。 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道(pipe)、命名管道(FIFO)与信号(Signal) System V IPC:消息队列、信号量、共享内存 Socket(支持不同主机上的两个进程IPC) 我们在这一讲介绍Unix IPC,中有关信号(Signal)的处理。...

最新的.Net面试题及答案

1:a=10,b=15,在不用第三方变题的前提下,把a,b的值互换   a=a+b;b=a-b;a=(a-b)/2;b=b+a2:已知数组int[] max={6,5,2,9,7,4,0};用快速排序算法按降序对其进行排列,并返回数组   using System; namespace VcQuickSort { /// <summary> /...

C#之虚函数

若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法。虚方法与非虚方法的最大不同是,虚方法的实现可以由派生类所取代,这种取代是通过方法的重写实现的(以后再讲)虚方法的特点:虚方法前不允许有static,abstract,或override修饰符虚方法不能是私有的,因此不能使用private修饰符虚方法的执行:我们知道一般函数在编译时就静态地...

JavaScript内置一些方法的实现原理--new关键字,call/apply/bind方法--实现

先学习下new操作符吧 new关键字调用函数的心路历程: 1.创建一个新对象 2.将函数的作用域赋给新对象(this就指向这个对象) 3.执行函数中的代码 4.返回这个对象 根据这个的思路,来实现一个简单的new操作吧,代码演示: 1 function myNew(Func, ...args) { 2 if (typeof Func !== 'fu...

OpenGL编程逐步深入(四)Shaders

OpenGl 中的 Shader在一些中文书籍或资料中都被翻译为“着色器”, 单从字面意思也看不出Shader到底是什么,Shader实际上就是一段代码,用于完成特定功能的一个模块。Shader分为Vertex Shader(顶点着色器)和Pixel Shader(像素着色器)两种,其中Pixel Shader在本文中又被称为Fragment Shade...