空基类优化empty base class optimization
1、为什么C++中不允许类的大小是0
class ZeroSizeT {};
ZeroSizeT z[10];
&z[i] - &z[j];
一般是用两个地址之间的字节数除以类型大小而得到的,而类型大小是0将会出问题
2、为什么有时多个类组成实例所占空间都是一样的

class Empty
{ }; class EmptyToo : public Empty
{ }; class EmptyThree : public EmptyToo
{ }; sizeof(Empty) : 1
sizeof(EmptyToo) : 1
sizeof(EmptyThree) : 1

空基类优化:只要不会与同一类型的另一个对象或子对象分配在同一地址,就不需要为其分配空间
1、为什么C++中不允许类的大小是0
class ZeroSizeT {};
ZeroSizeT z[10];
&z[i] - &z[j];
一般是用两个地址之间的字节数除以类型大小而得到的,而类型大小是0将会出问题
2、为什么有时多个类组成实例所占空间都是一样的

class Empty
{ }; class EmptyToo : public Empty
{ }; class EmptyThree : public EmptyToo
{ }; sizeof(Empty) : 1
sizeof(EmptyToo) : 1
sizeof(EmptyThree) : 1

空基类优化:只要不会与同一类型的另一个对象或子对象分配在同一地址,就不需要为其分配空间。
3、对于空基类优化,如何理解前提条件“只要不会与同一类型的另一个对象或子对象分配在同一地址”,以及why

class Empty
{ }; class EmptyToo : public Empty
{ }; class EmptyThree : public Empty, public EmptyToo
{ }; sizeof(Empty) : 1
sizeof(EmptyToo) : 1
sizeof(EmptyThree) : 2

NoEmpty的基类Empty和EmptyToo不能分配在同一地址空间,C++内存布局不允许相同类型的子对象偏移量相同。
对空基类优化进行限制的根本原因在于,我们需要能比较两个指针是否指向同一对象。由于指针几乎总是用地址作内部表示,因此必须保证两个不同的地址对应两个不同的对象。
更详细的:http://www.programlife.net/the-empty-base-class-optimization.html
附:
关于空基类优化的应用:在《C++ Templates The Complete Guide》e_CN的p_435中关于函数对象的组合问题,使用空基类优化的特性,将组合关系改成了继承关系。
What is empty class and why worth optimization
Empty class顾名思义就是空类,比如
- class empty {};
这里empty显然是一个空类,什么成员都没有。但是空类不限于这种形式,对于只有成员函数,并且没有non-static data member的类,也可以是空类。
- class empty
- {
- public:
- static void f();
- void f();
- };
- class empty_too : public empty {};
但是有一点需要注意,如果一个类或者他的基类中包含虚函数,那么该类就不是empty class,因为通常一个含有虚函数的类,都有一个vptr,所以就有了数据成员,虽然是隐藏的。
对于父类是虚基类的情况也是一样,因为一般子类需要一个指针指向虚基类的子对象。
对于一个空类,它的大小不是0,因为如果是0,那么两个对象就会拥有相同的地址,这样就无法简单用地址区分两个对象了。通常一个空类的大小可能是1,也可能是4,这取决于编译器。
如果一个类,它包含空类成员子对象,那么这就会造成一定的空间浪费,而这个浪费是可以避免的,因为有些编译器实现了一种称为empty base class optimization的优化。
标准中提到了这种优化:
10/5 Derived classes
A base class subobject may be of zero size (clause 9); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address (5.10).
基类子对象可以是大小为0的,但是限制是,两个相同类型的子对象不能分配在相同的地址上。所以技巧就是通过从空类来继承是实现一定的优化。
- class empty1 {};
- class empty2 {};
- class non_empty1
- {
- public:
- empty1 o1;
- empty2 o2;
- };
- class non_empty2
- : public empty1
- , public empty2
- {};
这里empty1,2是空类,通过继承,non_empty2的大小还是1。但是non_empty1的大小就是2。
需要注意的是,继承可能会带来对接口的影响,因为在泛型代码中,你不知道用户传入的类是否包含虚函数,如果包含虚函数,那么可能有一个与我们的类正好同名的虚函数,这样我们的函数就被虚化了。
解决这个问题可以不直接从空类继承,而是创建一个中间类,并让这种类来继承空类,这样可以将影响限制在我们的中间类中。并将这个中间类的对象作为成员保存。
- class empty {};
- class foo : public empty {}; // not always correct
- class foo
- {
- class bar : public empty {};
- // ok, the interface of foo is not affected by the inheritance from empty;
- };
在stl中,大量用到了函数对象,并且有许多函数对象是空的,如果大量存储这些函数对象也是会造成一定的浪费的(为什么要存储?假设一下,哈哈)。
在《C++ template metaprogramming》中有这样一个例子:有一个类,实现一个简单的复合函数f(g(x))
- template<typename R, typename F, typename G>
- class composed_fg
- {
- public:
- composed_fg(const F& f, const G& g)
- : f(f)
- , g(g)
- {}
- template<typename T>
- R operator ()(const T& t) const
- {
- return f(g(t));
- }
- private:
- F f;
- G g;
- };
这里如果f或者g是空类,那么就会造成空间的浪费,视编译器而定,composed_fg最多可能会在32-bit平台上占用8字节。但是我们进行空基类优化,当f或者g中有空基类时,我们选择不同的实现。
boost.compressed_pair就实现了一个优化过的std.pair,我们来分析一下boost.compressed_pair的实现。
compressed_pair根据T1, T2的类型,来选择不同的实现,有6种情况
| T1 == T2 | T1 empty | T2 empty |
| false | false | false |
| false | true | false |
| false | false | true |
| false | true | true |
| true | false | false |
| true | true | true |
其中区分T1==T2是因为,C++不允许有2个相同的直接基类。
What is empty class and why worth optimization
Empty class顾名思义就是空类,比如
[cpp] view plaincopyprint?
class empty {};
这里empty显然是一个空类,什么成员都没有。但是空类不限于这种形式,对于只有成员函数,并且没有non-static data member的类,也可以是空类。
[cpp] view plaincopyprint?
class empty
{
public:
static void f();
void f();
}; class empty_too : public empty {};
但是有一点需要注意,如果一个类或者他的基类中包含虚函数,那么该类就不是empty class,因为通常一个含有虚函数的类,都有一个vptr,所以就有了数据成员,虽然是隐藏的。
对于父类是虚基类的情况也是一样,因为一般子类需要一个指针指向虚基类的子对象。
对于一个空类,它的大小不是0,因为如果是0,那么两个对象就会拥有相同的地址,这样就无法简单用地址区分两个对象了。通常一个空类的大小可能是1,也可能是4,这取决于编译器。
如果一个类,它包含空类成员子对象,那么这就会造成一定的空间浪费,而这个浪费是可以避免的,因为有些编译器实现了一种称为empty base class optimization的优化。
标准中提到了这种优化:
/ Derived classes
A base class subobject may be of zero size (clause ); however, two subobjects that have the same class type and that belong to the same most derived object must not be allocated at the same address (5.10).
基类子对象可以是大小为0的,但是限制是,两个相同类型的子对象不能分配在相同的地址上。所以技巧就是通过从空类来继承是实现一定的优化。
[cpp] view plaincopyprint?
class empty1 {};
class empty2 {}; class non_empty1
{
public:
empty1 o1;
empty2 o2;
}; class non_empty2
: public empty1
, public empty2
{};
这里empty1,2是空类,通过继承,non_empty2的大小还是1。但是non_empty1的大小就是2。
需要注意的是,继承可能会带来对接口的影响,因为在泛型代码中,你不知道用户传入的类是否包含虚函数,如果包含虚函数,那么可能有一个与我们的类正好同名的虚函数,这样我们的函数就被虚化了。
解决这个问题可以不直接从空类继承,而是创建一个中间类,并让这种类来继承空类,这样可以将影响限制在我们的中间类中。并将这个中间类的对象作为成员保存。
[cpp] view plaincopyprint?
class empty {};
class foo : public empty {}; // not always correct class foo
{
class bar : public empty {};
// ok, the interface of foo is not affected by the inheritance from empty;
};
在stl中,大量用到了函数对象,并且有许多函数对象是空的,如果大量存储这些函数对象也是会造成一定的浪费的(为什么要存储?假设一下,哈哈)。
在《C++ template metaprogramming》中有这样一个例子:有一个类,实现一个简单的复合函数f(g(x))
[cpp] view plaincopyprint?
template<typename R, typename F, typename G>
class composed_fg
{
public:
composed_fg(const F& f, const G& g)
: f(f)
, g(g)
{} template<typename T>
R operator ()(const T& t) const
{
return f(g(t));
}
private:
F f;
G g;
};
这里如果f或者g是空类,那么就会造成空间的浪费,视编译器而定,composed_fg最多可能会在32-bit平台上占用8字节。但是我们进行空基类优化,当f或者g中有空基类时,我们选择不同的实现。
boost.compressed_pair就实现了一个优化过的std.pair,我们来分析一下boost.compressed_pair的实现。
compressed_pair根据T1, T2的类型,来选择不同的实现,有6种情况
T1 == T2 T1 empty T2 empty
false false false
false true false
false false true
false true true
true false false
true true true
其中区分T1==T2是因为,C++不允许有2个相同的直接基类。
这个是T1和T2都不为空的情况,这里只是简单地在对象中保存了2个成员对象。再来看一下其中一个为空的情况。
template <class T1, class T2>
class compressed_pair_imp<T1, T2, >
: protected ::boost::remove_cv<T1>::type
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference; compressed_pair_imp() {} compressed_pair_imp(first_param_type x, second_param_type y)
: first_type(x), second_(y) {} compressed_pair_imp(first_param_type x)
: first_type(x) {} compressed_pair_imp(second_param_type y)
: second_(y) {} void swap(::boost::compressed_pair<T1,T2>& y)
{
// no need to swap empty base class:
cp_swap(second_, y.second());
}
private:
second_type second_;
};
这里T1为空,compressed_pair从T1继承了,然而T2还是作为成员保存起来了。还有一点变化就是swap中,只对second进行了操作,很显然,因为T1子对象是空的,swap没有意义。
其他的情况类似了,所以可以自己去看boost的源码。
Side Note
VC中存在对Empty base class过度优化的情况,对于2个相同类型基类子对象的情况,在g++中,会生成2个字节大小的对象,而VC中只是1个字节的大小。
References
[1] The "Empty Member" C++ Optimization
[2] Empty Base Class or Structure Assignment Operator May Corrupt Data
[3] Understanding the Empty Base Optimization
[4] 《C++ template metaprogramming》
[5] Why is the size of an empty class not zero?
参考:http://blog.csdn.net/seizef/article/details/6168721
http://www.bubuko.com/infodetail-479676.html
http://www.cppblog.com/qinqing1984/archive/2011/07/10/150584.aspx
空基类优化empty base class optimization的更多相关文章
- 空基类优化—— EBCO—— empty base class optimization
完全参考自:<C++ Templates The Complete Guide>e_CN,p_281 16.2 空基类优化 最近周围有点吵,论文没看进去,随便翻了本书…… 下文没有多大意义 ...
- [百度空间] [原] Empty base class optimization
最近遇到了一个诡异的问题, 数组的数据不对, 最后发现是两个类型的大小不一样导致的. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
- C#继承机制 访问与隐藏基类成员
(1) 访问基类成员 通过base 关键字访问基类的成员: 调用基类上已被其他方法重写的方法. 指定创建派生类实例时应调用的基类构造函数. 基类访问只能在构造函数.实例方法或实例属性访问器中进 ...
- 不可或缺 Windows Native (22) - C++: 多重继承, 虚基类
[源码下载] 不可或缺 Windows Native (22) - C++: 多重继承, 虚基类 作者:webabcd 介绍不可或缺 Windows Native 之 C++ 多重继承 虚基类 示例1 ...
- 4.6 C++抽象基类和纯虚成员函数
参考:http://www.weixueyuan.net/view/6376.html 总结: 在C++中,可以通过抽象基类来实现公共接口 纯虚成员函数没有函数体,只有函数声明,在纯虚函数声明结尾加上 ...
- C++中的抽象基类示例
抽象基类(abstract base class,ABC)例子:圆与椭圆.建立一个基类BaseEllipse,建立它的恋歌继承了Ellipse和Circle.ellipse.h #ifndef ELL ...
- C# 关于接口与基类的理解(二者的区别)
接口(接口的名称一般用大写字母I开头的)是把公共实例(非静态)方法和属性组合起来,以封装特定功能的一个集合.(其实,接口简单理解就是一种约定,使得实现接口的类或结构在形式上保持一致) 注意:使用接口可 ...
- Python的程序结构[2] -> 类/Class[1] -> 基类与继承
基类与继承 / Base Class and Inheritance Class 面向对象的特性使得 Python 中不可避免地需要使用到类和类的继承,类的继承可以使得代码很好的被重用.下面以一些代码 ...
- C++派生类中如何初始化基类对象(五段代码)
今天收到盛大的面试,问我一个问题,关于派生类中如何初始化基类对象,我在想派生类对于构造函数不都是先构造基类对象,然后在构造子类对象,但是如果我们在成员初始化列表先初始化派生类的私有成员,在函数内去调用 ...
随机推荐
- redis数据类型List的安全队列和不安全队列
在学习RPOPLPUSH命令的时候,官方文档中有提到安全队列和不安全的队列,一开始没有看懂,现在理解了做个笔记. 一般情况下,我们可以借助List来实现消息队列,比如一个客户端通过命令LPUSH(BL ...
- ubox及日志管理
ubox是openwrt的帮助工具箱,位于代码package/system/ubox下, CMakeLists.txt kmodloader.c log/ lsbloader.c validate/ ...
- QT creator 编辑多个UI 文件 出现 无法解析的外部符号的错误
创建一般的Qt Gui 程序一般会默认一个UI 文件 ,但是随着应用程序窗口的增多,同时编辑多个UI 界面是必须的. 假设我们已经创建好了一个QTUI的工程,里面已经默认了一个UI文件,但是想在添几个 ...
- 第二百二十三节,jQuery EasyUI,ComboBox(下拉列表框)组件
jQuery EasyUI,ComboBox(下拉列表框)组件,可以远程加载数据的下拉列表组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解 EasyUI 中 C ...
- 一行代码解决各种IE兼容问题,IE6,IE7,IE8,IE9,IE10 (转)
x-ua-compatible 用来指定IE浏览器解析编译页面的model x-ua-compatible 头标签大小写不敏感,必须用在 head 中,必须在除 title 外的其他 meta 之前使 ...
- db2 设置表 not null
db2将原表列notnull属性修改为null属性的方法 今天把自己遇到的一个小问题跟大家分享一下如何修改db2数据库表中列的属性--将列的非空属性改为允许空的属性,修改数据表的某一列属性其实很简 ...
- memset陷阱
最近在实现差分进化算法的时候,发现数据异常,查了好久,才知道是memset惹的祸! #include <iostream> #include <cstring> using n ...
- ASP.NET Web API中的路由
ASP.NET Web API的默认路由在App_Start目录中的WebApiConfig.cs文件中定义的. public static class WebApiConfig { public s ...
- md5 算法类 java
package com.sunyard.p2p.util; import java.security.MessageDigest; import java.security.NoSuchAlgorit ...
- Linux ping不通百度的解决方法
今天在学习DNS的时候遇到了一个问题,我的虚拟机能够ping通ip地址,却ping不通www.baidu.com www.qq.com等域名,先是出现了以下报错: 折腾了几个小时终于找到解决办法 1. ...