请考虑以下代码:
#include <iostream>
#include <type_traits>
class Base
{
public: static int f() {return 42;}
};
class Derived : public Base
{
protected: int x;
};
class NotDerived
{
protected: int x;
};
int main()
{
std::cout<<sizeof(Base)<<std::endl;
std::cout<<sizeof(Derived)<<std::endl;
std::cout<<sizeof(NotDerived)<<std::endl;
return 0;
}
使用g ++ 4.7 -O3,它会打印:
1
4
4
如果我理解得那么,就意味着启用了空基类优化。
但是我的问题涉及运行时开销:由于Derived
应构造/破坏这一事实,与NotDerived
对象相比,是否存在创建(和破坏)Derived
对象的开销相应的Base
对象?
答案 0 :(得分:3)
虽然标准不能保证那里我会考虑编译器在这些情况下做了一些不同的东西,但是有点缺陷。
初始化基础实际上没有什么可做的:不需要初始化内存,也不需要设置虚拟调用机制。不应该为它生成代码。
但是,如果这对你来说非常重要,你应该总是在一个非平凡的环境中检查一些程序集。
答案 1 :(得分:2)
对此的任何答案都将取决于实现,因为标准只指定了语义。
然而,随着任何现代编译器和优化的开启,我希望看起来没有任何区别。
没有额外的内存可供分配,没有额外的代码可以运行,因为额外的基础不是虚拟的,所以在构造期间没有可改变的vtable指针。您的Derrived
和NotDerrived
构造函数可能与指令相同。
关闭所有优化后,您可能会在某些平台上调用空Base::Base()
函数,但您真的不应该担心未经优化的构建的性能。
我在gcc.godbolt.org上整理了一个小型演示:http://tinyurl.com/cg8ogym
简而言之
extern void marker______________________________________();
// ...
marker______________________________________();
new NotDerived;
marker______________________________________();
new Derived;
marker______________________________________();
编译到
call marker______________________________________()@PLT
movl $4, %edi
call operator new(unsigned long)@PLT
call marker______________________________________()@PLT
movl $4, %edi
call operator new(unsigned long)@PLT
call marker______________________________________()@PLT
如果将其切换为clang,它甚至会优化内存分配
答案 2 :(得分:0)
#include <type_traits>
#include <unistd.h>
class SlowBase
{
SlowBase() { ::sleep(5); }
public: static int f() {return 42;}
};
static_assert( std::is_empty<SlowBase>::value , "SlowBase is empty" );
Base
班需要五秒钟来构建 !!
#include <type_traits>
class FatBase
{
FatBase() = default;
int data[1024*1024];
public: static int f() {return 42;}
};
static_assert( !std::is_empty<FatBase>::value , "FatBase is not empty" );
但是这个没时间!
我的观点是构造开销与类的大小无关,它与构造函数的作用有关。 SlowBase
是一个空类,但构造起来很慢。 FatBase
的大小是兆字节,但是甚至没有将数组元素归零,因此无关。
在你的Base
示例中,有一个隐式声明的,简单的默认构造函数,因此它无关。