Empty class and optimization
来源:互联网 发布:国产篮球鞋知乎 编辑:程序博客网 时间:2024/06/05 03:52
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 correctclass 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 == T2T1 emptyT2 emptyfalsefalsefalsefalsetruefalsefalsefalsetruefalsetruetruetruefalsefalsetruetruetrue其中区分T1==T2是因为,C++不允许有2个相同的直接基类。
template <class T1, class T2>class compressed_pair_imp<T1, T2, 0>{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_(x), second_(y) {} compressed_pair_imp(first_param_type x) : first_(x) {} compressed_pair_imp(second_param_type y) : second_(y) {} // ... void swap(::boost::compressed_pair<T1, T2>& y) { cp_swap(first_, y.first()); cp_swap(second_, y.second()); }private: first_type first_; second_type second_;};这个是T1和T2都不为空的情况,这里只是简单地在对象中保存了2个成员对象。再来看一下其中一个为空的情况。
template <class T1, class T2>class compressed_pair_imp<T1, T2, 1> : 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?
- Empty class and optimization
- Understanding the Empty Base Optimization
- [笔记]C++ Empty Class(analysis construcor and desturctor)
- 2.13. Performance and Optimization
- Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor
- Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor
- empty string and null
- C++ - 空白基类最优化(empty base optimization, EBO) 详解
- 从Empty class 看 is_Empty
- C++ empty class default function
- Java Memory Management and Optimization
- Firefox optimization and troubleshooting thread
- swapniess and overcommit and other hadoop optimization
- 关于make sure class name exists, is public, and has an empty constructor that is public解决办法
- Unable to instantiate fragment XXX make sure class name exists, is public, and has an empty constru
- isset() and empty() in PHP
- Empty Matrices, Scalars, and Vectors
- optimization
- 8.7 ArrayList_HasSet 的比较及Hascode分析 和内存泄露
- 8.9反射的应用 实现框架的功能
- sort,qsort的一些用法。
- Web开发之一:Web UI技术综述
- 8.10 用类加载器的其他作用
- Empty class and optimization
- 2-最小化往返时间
- Web开发之二:什么是前端、什么是后端
- 9.1 用内省的方式反射JavaBean
- 最好用的屏幕截图抓屏工具(FastStone Capture) 绿色版下载,首选推荐!
- Web开发之三:前后端开发任务量分析与比较
- 9.2用BeanUtil工具包操作JavaBean
- Piwik 体系和源代码分析 --1
- 10.类加载器