2

考虑以下代码:

#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与对象相比是否有任何开销创建(和销毁)对象?NotDerivedDerivedBase

4

3 回答 3

3

虽然该标准不保证在那里我会考虑一个编译器在这些情况下做一些不同的事情略有缺陷。

从字面上看,不需要做任何事情来初始化基础:不需要初始化内存,不需要设置虚拟调用机制。不应为其生成任何代码。

但是,如果这对您来说真的很重要,您应该始终在非平凡的设置中检查某些程序集。

于 2012-12-18T00:15:09.843 回答
2

对此的任何答案都将取决于实现,因为标准仅指定语义。

但是,打开任何现代编译器和优化,我希望完全没有区别。

没有要分配的额外内存,没有要运行的额外代码,在构造过程中没有要更改的 vtable 指针,因为额外的基础不是虚拟的。您DerrivedNotDerrived构造函数很可能是指令对指令相同的。

关闭所有优化后,您可能会在某些平台上调用空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,它甚至会优化内存分配

于 2012-12-18T00:13:39.903 回答
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示例中,有一个隐式声明的微不足道的默认构造函数,因此它无关紧要。

于 2012-12-18T10:12:04.143 回答