假设我有一个类
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
的类型。此外,我希望编译器将优化构造函数内的其他分支。如果有人知道这是真的还是如何检查,那将非常感激。我还假设投入模板进入情境不会改变上面的“空类”问题,是吗?
答案 0 :(得分: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";
}
此优化是安全的,可能最有用。它允许程序员使用空类来表示非常简单的概念而不会产生开销。一些当前的编译器提供了这种“空基类优化”。
答案 1 :(得分:2)
根据具体情况,它可能,也可能不是。如果你说:
Empty e;
Empty * ep = & e;
然后显然必须分配东西。
答案 2 :(得分: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
函数相比较,创建了8个Empties。使用x86上的g ++,如果你确实获取了任何空口的地址,你就会得到堆栈中x和a之间的位置,因此在输出中包含x。您不能假设对象的存储最终将与源代码中声明的顺序相同。