C++ 类继承

类的继承分为3类:单继承,多继承和虚继承。

1. 单继承
class B {
public:
  int b1;

  B(int init) {
    b1 = init;
  }
};

class D : public B1 {
public:
  int d1;

  D(int init)
  : B1(init - 1)
  {
    d1 = init;
  }
};

int main()
{
  D d(5);
}

这时在内存中可以看到先分配了Base类的空间,这里是4B。关于类大小的计算执行百度。

150409_jjo9_1580806.png

2. 多继承
class B1 {
public:
  int b1;

  B1(int init) {
    b1 = init;
  }
};

class B2 {
public:
  int b2;

  B2(int init) {
    b2 = init;
  }
};

class D : public B1, public B2 {
public:
  int d1;

  D(int init)
  : B1(init - 1)
  , B2(init - 2)
  {
    d1 = init;
  }
};

这时的内存图为:

151157_uBvY_1580806.png

现在单继承和多继承有点了解了,那么如果多继承中的2个基类 B1, B2 又同时继承于类 A,这时内存是如何表现的。

class A {
  public:
    int a1;
    A(int init) { a1 = init; }
};

class B1 : public A { ... };
class B2 : public A { ... };

/* 这里的流程如上面的多继承 */

内存图:

152835_Gbos_1580806.png

这样的静态分配看出没有任何的问题,我们使用指针去访问类A的变量a1。

D d(5);
// A *p = (A *)&d; // 语法错误
B1 *p1 = (B1 *)&d;
A *p = (A *)p1;
cout << p->a1 << endl;  // 3

B2 *p2 = (B2 *)&d;
p = (A *)p2;
cout << p->a1 << endl;  // 2

// d.a1 = 1; // 这时会报错,因为这个时候不知道指定的a1变量是哪个。

擦,这个编译器还是可以根据对应的继承关系找到对应的值的。叼,这里有个问题,因为现在我们都带了初始化的变量,这时从B1, B2分别实例化的A具有不同的值,但是如果我们想想这种情形。如果类A中是一些需要实现的方法,按这种方式,我们需要分别在 B1 , B2中分别去实现 A 的方法,如果需要实现的类方法是有差异的,那么这样的做法就是正确的。但是如果我们需要实现的类方法是一样的,那么这里A的每个方法在内存中就有重复的2个版本,这样就造成了空间的浪费。这里可以使用虚继承。

3. 虚函数
class B {
  public:
    int b;
    B(int init) { b = init; }
    void Print() { cout << b << endl; }
    virtual void vPrint() { cout << b << endl; }
};

class D : public B {
  public:
    int d;
    D(int init):B(init-1) { d = init; }
    void Print() { cout << d << endl; }
    virtual void vPrint() { cout << d << endl; }
};

int main() 
{
  D d(5);
  B *pb = (B *)&d; // 使用基类指针
  
  d.Print();    // 5
  pb->Print();  // 4
  pb->vPrint(); // 5
}

内存图:

165102_aLwA_1580806.png

可以看到,类D的实例化变量 d 的大小为12,只包含了 本身和基类的变量和一个虚函数指针的地址。这里应该称为虚函数表的地址指针。

虚函数在继承的时候会发生覆盖,这从程序的执行结果和内存图中都可以看到。而对于类中的一般函数,则没有覆盖现象的发生。

如果在基类中我们只定义一些函数,而不去实现它,这时就需要使用到纯函数,这种一般都是让这些函数在派生类中去实现,这样可以称为接口。

virtual void Print()=0; // 纯虚函数定义  virtual [返回类型] [函数名]([变量列表]) = 0;

当类中又这样的纯虚函数定义存在的时候,这时的类称为抽象类,在语法上是不允许被实例化的。因为其中的函数需要派生类去实现。

4. 虚继承
class A {
  public:
    int a;
    A() { a = 3; }
};

class B1 : virtual public A {
  public:
    int b1;
    B1() { b1 = 4; }
};

class B2 : virtual public A {
  public:
    int b2;
    B2() { b2 = 5; }
};

class C : virtual public B1, virrual public B2 {
  public:
    int c;
    C() { c = 6; }
};

int main()
{
  C c;
  return 0;
}

内存图:

175651_r83U_1580806.png

内存的分配还是有点不清楚。

转载于:https://my.oschina.net/kuangcaibao/blog/398130

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值