9

假设我有一个像

class Empty{
    Empty(int a){ cout << a; }
}

然后我使用它调用它

int main(){
    Empty(2);
    return 0;
}

这会导致在堆栈上分配任何内存以创建“空”对象吗?显然,参数需要被压入堆栈,但我不想产生任何额外的开销。基本上我将构造函数用作静态成员。

我想这样做的原因是因为模板。实际代码看起来像

template <int which>
class FuncName{
    template <class T>
    FuncName(const T &value){
        if(which == 1){
            // specific behavior
        }else if(which == 2){
            // other specific behavior
        }
    }
};

这让我可以写类似的东西

int main(){
    int a = 1;
    FuncName<1>(a);
}

这样我就可以专门化一个模板参数,而不必指定T. 另外,我希望编译器能够优化构造函数内的其他分支。如果有人知道这是否属实或如何检查,将不胜感激。我还假设将模板放入这种情况并不会改变上面的“空类”问题,对吗?

4

3 回答 3

17

引用 Stroustrup 的话:

为什么空类的大小不为零?保证两个不同对象的地址会不同。出于同样的原因,“new”总是返回指向不同对象的指针。考虑:

class Empty { };

void f()
{
    Empty a, b;
    if (&a == &b) cout << "impossible: report error to compiler supplier";

    Empty* p1 = new Empty;
    Empty* p2 = new Empty;
    if (p1 == p2) cout << "impossible: report error to compiler supplier";
}   

有一条有趣的规则说空基类不需要用单独的字节表示:

struct X : Empty {
    int a;
    // ...
};

void f(X* p)
{
    void* p1 = p;
    void* p2 = &p->a;
    if (p1 == p2) cout << "nice: good optimizer";
}

这种优化是安全的并且可能是最有用的。它允许程序员使用空类来表示非常简单的概念而没有开销。当前的一些编译器提供了这种“空基类优化”。

于 2010-01-06T21:45:38.400 回答
2

它可能,它可能,不是,取决于具体情况。如果你说:

Empty e;
Empty * ep = & e;

那么显然必须分配东西。

于 2010-01-06T21:39:03.680 回答
2

试试看。当要求优化其输出时,许多编译器会消除此类临时对象。

如果反汇编过于复杂,则创建两个具有不同数量的此类对象的函数,并查看它们周围的对象的堆栈位置是否有任何差异,例如:

void empty1 ( int x )
{
    using namespace std;

    int a;
    Empty e1 ( x );
    int b;

    cout << endl;
    cout << "empty1" << endl;
    cout << hex << int ( &x ) << " " << dec << ( &x - &a ) << endl;
    cout << hex << int ( &a ) << " " << dec << ( &a - &b ) << endl;
}

然后尝试运行它与empty8创建八个 Empties 的函数进行比较。在 x86 上使用 g++,如果您确实获取了任何空的地址,您将在堆栈上获得 x 和 a 之间的位置,因此在输出中包括 x。您不能假设对象的存储最终会按照它们在源代码中声明的顺序结束。

于 2010-01-06T21:44:53.500 回答