c++继承

来源:互联网 发布:elton john知乎 编辑:程序博客网 时间:2024/06/11 12:37

什么是继承

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。

继承的语法

在代码中和原来一样给出该类的名字,但在类的左括号前面,加上一个冒号和基类的名字(多重继承基类名用逗号分开)

继承的关系

继承方式 基类的public成员 基类的protect成员 基类的private成员 public 仍为public成员 仍为protected成员 不可见 protected 变为protected成员 仍为protected成员 不可见 private(默认) 变为private成员 变为private成员 不可见

对私有继承成员公有化

当私有继承时所有的public成员都变成了private,如果希望它们中任何一个是可视的,只需要用派生类的public部分生命他们的名字即可。

c语言实现继承

/// \file inherit.c/// \brief/// \author suntangji, suntangj2016i@gmail.com/// \version 1.0/// \date 2017-11-24#include<stdio.h>#include<malloc.h>struct Base {    int b;    void (*pfb)();    /*c语言不支持结构体内部定义函数,所以使用函数指针 */};struct Derived {    int d;    void (*pfd)();    struct Base* base;};void FunBase() {    printf("Base::FunBase");}void FunDerived() {    printf("Derived::FunDerived");}struct Base* NewBase(){    struct Base* pb = (struct Base*)(malloc(sizeof(struct Base)));    pb->b = 0;    pb->pfb = FunBase;    return pb;}struct Derived* NewDerived(){    struct Derived* pd = (struct Derived*)(malloc(sizeof(struct Derived)));    pd->d = 0;    pd->pfd = FunDerived;    pd->base = NewBase();     return pd;}int main() {    struct Base * b = NewBase();    struct Derived * d = NewDerived();    b->pfb();    d->pfd();    d->base->pfb();    return 0;}

C语言实现的缺陷

  • 没有继承关系
  • 没有构造函数和析构函数容易造成内存泄露
  • 函数调用比较麻烦

构造函数的调用顺序

先调用派生类的构造函数,在派生类构造函数参数列表中调用基类的构造函数,然后在构造派生类自己的成员。

析构函数的调用顺序

先调用派生类的析构函数,再调用基类的析构函数。

同名隐藏与重写

当基类的成员函数不是虚函数时,派生类和它有同名的函数时(函数原型可以不同)会隐藏基类的函数。当基类的成员函数是虚函数时,派生类有和基类同名的函数(函数原型需要相同)会发生重写。

向上类型转换

  • 派生类对象可以赋值给基类对象
  • 基类对象不可以赋值给派生类对象
  • 基类对象的指针/引用可以指向派生类对象,但不能访问派生类对象可以访问基类对象
  • 派生类对象的指针/引用不可以指向基类对象(强制类型转换后可以)

非自动继承的函数

  • 构造函数
  • 析构函数
  • 赋值运算符重载

单继承的对象模型

基类的成员在上(低地址),派生类的成员在下(高地址)

多继承的对象模型

最先继承的基类在上(低地址),其次继承的基类在下(高地址),派生类的成员在最下(低地址)

菱形继承的对象模型

class Base {public:    Base() {        _b = 1;    }private:    int _b;};class C1:public Base{public:    C1() {        _c1= 2;    }private:    int _c1;};class C2 :public Base{public:    C2() {        _c2 = 3;    }private:    int _c2;};class Derived :public C1,public C2{public:    Derived() {        _d = 4;    }private:    int _d;};

这段代码在内存中存储是1 2 1 3 4
可以看出仍然是派生类的成员在下,基类的成员在上,只是基类又是其他基类的派生类。菱形继承对最上层的基类继承了2次,既造成了二义性又浪费了空间。

虚拟继承

虚拟继承可以解决菱形继承二义性的问题

class Base {public:    Base() {        _b = 1;    }private:    int _b;};class C1:virtual public Base{public:    C1() {        _c1= 2;    }private:    int _c1;};class C2 :virtual public Base{public:    C2() {        _c2 = 3;    }private:    int _c2;};class Derived :public C1,public C2{public:    Derived() {        _d = 4;    }private:    int _d;};

对象d的内存布局

0x0115FD44 &nbsp e8 8b db 00
0x0115FD48 &nbsp 02 00 00 00
0x0115FD4C &nbsp f0 8b db 00
0x0115FD50 &nbsp 03 00 00 00
0x0115FD54 &nbsp 04 00 00 00
0x0115FD58 &nbsp 01 00 00 00

虚拟继承解决了菱形继承的二义性问题,因为菱形继承不再保存两份基类的变量,在对象内存的最高地址保存虚拟继承的基类成员,原来保存成员的地方保存了一个虚基类表格地址,该地址存储了2个变量,分别是相对自己的偏移量0和相对别虚拟继承的基类成员的偏移量。有了这两个偏移量就可以找到唯一一个基类成员,继而解决了菱形继承的二义性问题。