空白基类优化 ( Empty base optimization )

1、C语言中的空结构体

曾对C语言中的空结构体进行了简单分析(直达

 

2、空类对象的大小

下面输出什么?

#include <iostream>

using namespace std;

class Base
{
	
};

int main()
{
	cout << sizeof(Base) << endl;
	
	return 0;
}

VS2015

g++(7.3.0 or 4.4.0)

g++(4.45)

BCC55(标准C++)

下面看看对于这种现象的解释

The size of any object or member subobject (unless [[no_unique_address]] -- see below) (since C++20) is required to be at least 1 even if the type is an empty class type (that is, a class or struct that has no non-static data members), in order to be able to guarantee that the addresses of distinct objects of the same type are always distinct.

However, base class subobjects are not so constrained, and can be completely optimized out from the object layout:

- https://cppreference.com

蓝色画线部分清晰地说明了:

任何对象或成员子对象的大小要求至少为1,即便该类型是空类类型

以便能够保证相同类型的不同对象的地址始终是不同的。

橙色部分指出特殊情况:

基类子对象不是那么受约束,可以从对象布局中完全优化

- 空白基类优化

 

3、实验分析

空白基类优化  empty.cpp

#include <iostream>
#include <cassert>

using namespace std;
 
class Base {}; // empty class
 
class Derived : Base 
{
    int i;
};
 
int main()
{
    // the size of any object of empty class type is at least 1
    assert(sizeof(Base) >= 1);

    cout << sizeof(Base) << endl;
    cout << sizeof(Derived) << endl;
 
    // empty base optimization applies
    assert(sizeof(Derived) == sizeof(int));

    return 0;
}
 

在VS2015,g++ 7.3.0 or g++ 4.4.0 or g++ 4.4.5

   编译运行成功即断言正确:空基类对象大小至少为1,空白基类会优化

在Borland C++ 5.5.1 (标准C++)

22行断言失败,即:空基类对象大小至少为1,没有进行空白基类会优化

至于为什么派生类得到16  :  成员i偏移地址为12 + 大小为4 = 16    内存对齐(直达

 

   实验结论:

任何对象或成员子对象的大小要求至少为1,即便该类型是空类类型

空白类作基类时可能进行优化  -  空白基类优化

空白类的大小是未定义的,以及空白类作基类是否优化也是未定义的

现代编译器大多将空白类大小置为1,并进行空白基类优化

 

4、进一步分析

什么时候空白类作基类不进行优化?

        1、已经实验得知:如一些古老编译器BCC55不会进行空白基类优化

        2、基类在派生类中产生了实例(基类不被static修饰)

        3、多重继承中作基类(可能)

        4、使用属性关键字 [[no_unique_address]](C++20)

 

5、编程实验

空白类作基类不进行优化   empty.cpp

基类在派生类中产生实例

#include <cassert>
#include <iostream>

using namespace std;


class Base	
{
	// empty class
};

class Derived1 : Base 
{
	int i;
};

class Derived2 : Base 
{
	Base c; //若在此处用static修饰,结果为1,4,8,12
	int i;
};

class Derived3 : Base 
{
	Derived1 c; 
	int i;
};

int main()
{
	cout << sizeof(Base) << endl; 
	cout << sizeof(Derived1) << endl; //优化
	cout << sizeof(Derived2) << endl; //未优化
	cout << sizeof(Derived3) << endl; //优化


	assert(sizeof(Derived2) == 2 * sizeof(int));

	assert(sizeof(Derived3) == 2 * sizeof(int));

	return 0;
}

VS2015

 

多重继承作基类

VS2015

                           没有优化  

g++ 4.4.0

                                优化

扩展[[no_unique_address]](C++20)

#include <cassert>
#include <iostream>

using namespace std;

class A {}; // empty class

class B 
{
	int i;
	[[no_unique_address]] A a;
};

int main()
{
	assert(sizeof(A) >= 1);

	cout << sizeof(A) << endl;
	cout << sizeof(B) << endl;

	assert(sizeof(B) == sizeof(int)); //必须支持此属性关键字才可以可能被优化

	return 0;
}

 

6、面试中的空类

如果考虑更多,对于“空类”类中含有

      1、静态成员变量(放置静态存储区),成员函数

      2、虚函数(类中加上指向虚函数表的指针(32or64位))

      3、一个空类也一定有构造函数,析构函数,拷贝构造函数,赋值操作符重载(.text

 

7、应用

空基优化通常用于标准库类(std::vector, std::function, std::shared_ptr, etc)

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是定义一个基类 `baseclass` 的 C++ 代码: ``` class baseclass { public: virtual void someFunction() { // 这里是函数的实现代码 } }; ``` 这里定义了一个名为 `baseclass` 的基类,并且其中包含了一个名为 `someFunction` 的虚函数。在 C++ 中,使用 `class` 关键字来定义类,其中 `public:` 后面的代码表示该类的公有成员。虚函数的定义方式是在函数声明前加上关键字 `virtual`,这样派生类可以重写这个函数。 ### 回答2: 在面向对象编程中,基类是一种通用的类,通过其定义一些共通的属性和方法,然后由其它派生类来继承和扩展,从而减少重复的代码,简化程序的复杂度,也提高代码的复用性。因此,在代码编写时,首先定义一个基类是非常必要的。 Baseclass基类的定义如下: 1. 首先,定义一个类名为Baseclass。 2. 定义类的属性,包括变量和方法,并设置默认初始值。这些属性可以被派生类继承,在其它派生类中也可以重新定义。 3. 设置构造函数和析构函数。构造函数用来初始化对象,在对象创建时被调用,而析构函数则在对象销毁时被调用,用来释放资源。 4. 在基类中,也可以定义一些虚成员函数(virtual,用虚函数的原因说起会超过300字,这里不展开讲),以方便在派生类中重写,实现多态性的作用。虽然在基类中实现纯虚函数(abstract)没有意义,但是也可以定义它。 5. 派生类继承Baseclass,通过派生类对基类的方法和属性进行继承和重写,从而实现更复杂的功能。 6. 在基类中可以定义一些静态成员,这些成员变量和成员方法归类,不依赖对象自然执行,而是在程序启动时自动执行。 总之,定义一个基类的主要目的是通过封装、继承和多态等特性,让程序更加简洁、易懂、易扩展和改变。 ### 回答3: 基类是面向对象程序设计中非常重要的概念之一。它是一个通用的类模板,可以作为其他类的父类,被继承而形成子类。在基类中定义一些通用的方法和属性,这些方法和属性可以被子类直接继承和使用。基类可以实现多态和抽象等高级特性,为程序设计带来很多便利性和灵活性。 在C++中,我们可以通过定义一个基类baseclass,来实现基类的功能。基类baseclass可以包含一些数据成员和成员函数,这些成员函数可以被其它的派生类继承和使用。基类通常是一个抽象的类,它的成员函数可以被派生类重写,以适应具体的需求。 基类的定义需要注意以下几点: 1. 基类中的成员函数需要加上虚函数关键字“virtual”,以实现多态功能。 2. 声明基类的时候,需要使用关键字“class”或“struct”。 3. 基类可以定义成抽象类,其中可以包含纯虚函数。 4. 基类的析构函数需要定义成虚函数,以确保派生类的析构函数能够被正确调用。 以下是一个基类的定义示例: class baseclass { public: virtual void func1(); virtual void func2(); virtual ~baseclass(); private: int data; }; 其中,func1和func2为两个虚函数,其余成员是普通的数据和函数。基类的成员函数可以在派生类中被重写,以实现多态功能。在使用基类生成一个实例的时候,可以通过指向基类的指针来调用不同派生类的同名函数。基类的定义是面向对象程序设计中的一个非常基础的概念,只有了解了基类的定义和用法,才能更好地理解派生类和多态等进阶特性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值