虚函数,虚继承(以及内存分布,解决二义性问题)

本文探讨了C++中的虚函数概念及其作用,包括多态性、构造函数、内联函数、静态函数和友元函数为何不能是虚函数。特别强调了析构函数作为虚函数的重要性,以防止内存泄漏。此外,介绍了纯虚函数和抽象类的概念,以及它们在继承中的应用。同时,文章详细分析了C++对象内存占用情况,包括内存对齐规则,并通过实例展示了普通对象与虚继承对象的内存分布差异。
摘要由CSDN通过智能技术生成

虚函数

    虚函数的存在说明了C++的多态性

实现的方法:父类指针指向了子类对象,调用虚函数,调用的是子类的虚函数

(如果没有虚函数,那么无论用父类指针怎么调用函数,会永远调用父类函数,因为这是静态联编)

只有通过基类指针或者引用去调用虚函数,才会引发动态联编

基类中的虚函数,在派生类中也是虚函数,即使没有virtual

   什么函数不能是虚函数(只有成员函数才能是虚函数)

    1.    构造函数        因为虚函数是对象创建出来后动态联编的,而对象的创建是通过构造函数,这里没有调用构造函数创建对象,就让构造函数为虚函数,在逻辑上明显不符

    2.    内联函数        因为是动态联编,内联函数是代码替换,代码膨胀,代码替换是静态联编

    3.    静态函数        静态函数不属于任何对象,所以无法使用对象去调用静态函数

    4.    友元函数        上面说过,只有成员函数才能是虚函数,友元函数是普通的函数,不是成员函数

在这里析构函数可以是虚函数,原因是:

        基类指针指向子类对象,是多态常见的做法,那么delete基类指针去释放空间,也是常见的做法,但是在这里,delete基类指针只会调用

基类的析构函数,子类的析构函数不会被调用,所以子类的内存可能会发生泄露,所以在这里,将基类的析构函数,设置为虚函数,delete基类指针时,会同时调用派生类的析构函数

纯虚函数

纯虚函数的声明必须出现在类内,我们也可以为纯虚函数提供定义,不过函数体必须定义在类的外部,也就是说

    我们不能再类的内部为一个 =0 的函数提供函数体

抽象类

含有纯虚函数的类是抽象基类,抽象基类负责定义接口,而后续的其他类可以覆盖接口,

我们不能创建出一个抽象基类的对象

派生类中必须实现基类中的纯虚函数,否则他仍将被看做为一个抽象类

----------------------------------------------------------------------------------------------------------------------------------------------------------------

内存分布

1 普通的C++对象内存占用情况是什么样的?

一般简单情况,比较好推断,但是下面的情况,就不好推断了

<wiz_code_mirror>

class A
{
public:
    int m;
    double n;
    char a;
};
class B
{
public:
    int m;
    char a;
    double n;
};
int _tmain(int argc, _TCHAR* argv[]) {
    printf("%d\n", sizeof(A));
    printf("%d\n", sizeof(B));
    return 0;
}

输出的结果是:

24

16

同样的成员,但是占用内存的大小不同,是什么原因呢?

内存排布的规则是:

1 有一个默认的对齐数,假设是A。

2 排布每一个成员的时候,成员大小和A取其中的最小值,假设是N。

3 成员起始偏移,应该为N的整数倍。

4 最终对象的大小还应该是每一个N中最大值的整数倍。

根据以上规则分析class A

0~3      int m;

4~7      空出来了

8~15    double n;

16        char a;

17~23  为了规则4整体对齐空出来的。

根据以上规则分析class B

0~3      int     m;

4          char  a;

5~7      空出来的

8~15    double n;

所以说A的大小是24字节,B的大小是16字节。

在继承关系中,依然符合上面的规则:

<wiz_code_mirror>

class A
{
public:
    int m;
    double n;
    char a;
};
class B :virtual public A
{
public:
    int o;
    int p;
    int q;
};
int _tmain(int argc, _TCHAR* argv[]) {
    printf("%d\n", sizeof(B));
    return 0;
}

A是24,B是12,但是合在了一起大小却是40.

下面我们来考虑虚继承的情况

当一个类虚继承自另一个类的时候:

class A
{
public:
    A():l(0),m(1),n(2)
    {
    }
public:
    int l;
    int m;
    int n;

};
class B :virtual public A
{
public:
    B() :o(3), p(4), q(5)
    {
    }
public:
    int o;
    int p;
    int q;
};

效果就是多了一个虚基类表

 当多个虚继承的时候,如下面的代码:

#include <iostream>
using std::cout;
using std::endl;
class A
{
public:
    A():l(0),m(1),n(2)
    {
    }
public:
    int l;
    int m;
    int n;

};
class B :virtual public A
{
public:
    B() :o(3), p(4), q(5)
    {
    }
public:
    int o;
    int p;
    int q;
};
class C :virtual public A
{
public:
    C() :x(6), y(7), z(8)
    {
    }
public:
    int x;
    int y;
    int z;
};
class D:public B, public C
{
public:
    D() :a(9), b(10)
    {
    }
public:
    int a;
    int b;
};
int _tmain(int argc, _TCHAR* argv[]) {
    D obj;
    printf("%d\n", sizeof(D));
    return 0;
}

 假如没有虚继承的话

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jh035512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值