对象的存储空间 || 虚析构函数

对象存储空间的探讨

在实际了解对象的存储空间之前,相信大家也或多或少在某些地方看到过介绍有关一个对象的存储空间有多大,但有没有深入去了解关于一个对象为什么需要这么大的内存空间?到底是什么导致一个对象占了这么大的内存空间?
让我们通过几个简单的例子来先了解一下一个对象所占用的内存空间

简单test

  1. 空类存储空间的计算:
#include <iostream>
using namespace std;
class TEST {
};
int main()
{
        TEST test;
        cout << sizeof(test) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
上述定义了一个空类,没有任何成员,得到结果为1;空类型对象不包含任何信息,大小应该为0,但当我们声明了该类型的对象时,它必须在内存中占有一定的空间,否则无法使用这些对象。至于占用多少内存,由编译器决定

  1. 只有成员变量的类的计算:
#include <iostream>
using namespace std;
class TEST {
        int a,b,c;
};
int main()
{
        TEST test;
        cout << sizeof(test) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
这个应该很清楚,类中有3个int型变量,所以占12bytes,那么静态成员变量呢?

  1. 有成员变量及静态成员变量的计算:
#include <iostream>
using namespace std;
class TEST {
        int a,b,c;
        static int d;
};
int main()
{
        TEST test;
        cout << sizeof(test) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
这个结果与上述不加静态成员变量一致,所以得出静态成员变量不占内存空间

  1. 类中只有一个成员函数的存储空间计算:
#include <iostream>
using namespace std;
class TEST {
        int foo();
};
int main()
{
        TEST test;
        cout << sizeof(test) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
这个结果与空类是一致的,所以得出成员函数是不占内存空间的

  1. 类中构造函数、析构函数的空间占用情况:
#include <iostream>
using namespace std;
class TEST {
public:
        TEST() {};
        ~TEST() {};
};
int main()
{
        TEST test;
        cout << sizeof(test) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
与空类对象一致,得出构造函数与析构函数也是不占空间的

  1. 类中含有虚析构函数:
#include <iostream>
using namespace std;
class TEST {
public:
        TEST() {};
        virtual ~TEST() {};
};
int main()
{
        TEST test;
        cout << sizeof(test) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
类中含有一个构造与一个虚析构函数,得出结果为8。由上述可知构造函数不占内存空间,那么便是由虚析构函数占用了8bytes的内存

  1. 继承空类与多重继承空类存储空间的计算:
#include <iostream>
using namespace std;
class TEST {
};
class B {
};
class C : public TEST {
};
class D : public virtual B {
};
class E : public TEST, public B {
};
int main()
{       
        TEST test;
        B b;
        C c;
        D d;
        E e;
        cout << "sizeof(test) : " << sizeof(test)  << endl;
        cout << "sizeof(b) : " << sizeof(b) << endl;
        cout << "sizeof(c) : " << sizeof(c) << endl;
        cout << "sizeof(d) : " << sizeof(d) << endl;
        cout << "sizeof(e) : " << sizeof(e) << endl;
        return 0;
}

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
从上面结果可知,单一继承的空类空间为1,多重继承的空类空间为1,但虚继承得到的空类空间为8bytes

------------------------------------总结----------------------------------------
从上面结果可以得到,普通的成员函数与静态数据成员都是不占内存空间的,但当涉及到虚函数以及虚继承时就需要占用内存空间,且为8bytes,那么这是为什么呢?
这就要从C++的对象模型来分析,当涉及到虚函数与虚继承时,类会产生一堆指向虚函数的指针,放在表格中,这个表格被称为vtbl,而每个类对象会产生一个vptr(虚指针),指向这个形成的虚表,这个指针在64位机器中占8bytes,所以涉及到虚函数以及虚继承时会占用额外的空间,带来额外的负担
关于C++对象模型,了解更多深度探索C++对象模型

由此得出:每个对象所占用的存储空间只是该对象的非静态数据成员的总和

虚析构函数

今天在看书的时候发现了自己不太清楚的一个盲区,就是有关虚析构函数,而且涉及到内存泄漏问题,所以就在这里提一下
书中提到:当派生类对象经由一个基类指针被删除,而该基类带着一个non-virtual析构函数,会导致对象的派生类部分没被销毁掉,从而造成内存泄漏

以下举个例子来说明该问题:

#include <iostream>
using namespace std;
class Base {
public:
        Base() { cout << "Base::Base()" << endl; }
        ~Base() { cout << "Base::~Base()" << endl; }
};      
class Derive: public Base {
public: 
        Derive() { cout << "Derive::Derive()" << endl; }
        ~Derive() { cout << "Derive::~Derive()" << endl; }
};      
int main()
{       
        Base *pBase = new Derive();
        delete pBase;
        return 0;
} 

得到结果(运行环境:ubuntu 18.04, GCC 7.3.0):
在这里插入图片描述
从所得结果来看,析构函数只做了局部销毁工作,这可能形成资源泄漏、损坏数据结构等问题,那么怎样解决呢?
很简单,将基类的析构函数声明为虚函数,利用多态的性质实现将两个都析构

将上述代码中Base的析构函数改为虚函数,得到运行结果:
在这里插入图片描述

--------------------------------------------get-----------------------------------
对象的内存空间大小
虚析构函数

-----------------------------------------19.1.23日-----------------------------------------
牛客网习题:

#include <iostream>
using namespace std;
class Test {
public:
	int a,b;
	virtual void fun() {}
	Test(int temp1 = 0, int temp2 = 0) {
		a = temp1;
		b = temp2;
	}
	int getA() {
		return a;
	}
	int getB() {
		return b;
	}
};
int main() {
	Test obj(5, 10);
	int *pInt = (int*)&obj;
	*(pInt + 0) = 100;  //交换虚表指针,注意类中含虚函数
	*(pInt + 1) = 200;  //交换a的值
	cout << "a = " << obj.getA() << endl;
	cout << "b = " << obj.getB() << endl;
	return 0;
}

这种情况下应优先考虑虚表的存储地址,像在该题中,首先交换的是虚表的指针,其次交换a的值,由此分析可以得到 a = 200 , b = 10 a = 200, b = 10 a=200b=10

测试(64位机, s u b l i m e sublime sublime):
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值