我有一个通用类,用于不同的上下文 - 有时候用作 静态变量,有时作为堆栈/堆上的普通变量。
当它用作普通变量时,析构函数必须在调用时调用 超出范围 - 正常。可执行文件用于嵌入式目标中 闪存是一种有限的资源,永远不会退出,为此我想 这个"退出"要禁用的代码。
以下是举例说明问题。 A
是其中的类
正常情况下需要析构函数,但静态不需要析构函数
变量
struct Abstract {
virtual ~Abstract() {}
};
struct A : public Abstract {
int i = 0;
};
static A a;
static A b;
以下是由http://goo.gl/FWcmlu生成的汇编代码(由-Os -std=c++11
-fno-exceptions -fno-rtti
编译)
Abstract::~Abstract():
ret
A::~A():
ret
A::~A():
jmp operator delete(void*)
Abstract::~Abstract():
jmp operator delete(void*)
pushq %rax
movl $__dso_handle, %edx
movl a, %esi
movl A::~A(), %edi
call __cxa_atexit
popq %rcx
movl $__dso_handle, %edx
movl b, %esi
movl A::~A(), %edi
jmp __cxa_atexit
vtable for Abstract:
vtable for A:
b:
.quad vtable for A+16
.long 0
.zero 4
a:
.quad vtable for A+16
.long 0
.zero 4
如上面的汇编代码所示,发布了相当数量的指令 这样做可以清理代码。
是否可以执行任何操作来禁用此不需要的清理代码?它不需要便携式 - 只要它适用于最新版本的GCC。最受欢迎的是属性,链接器脚本,更改目标文件和其他技巧。
答案 0 :(得分:2)
答案是创建一个包装器:
template<class T>
class StaticWrapper
{
public:
using pointer = typename std::add_pointer<T>::type;
template<class... Args>
StaticWrapper(Args && ...args)
{
new (mData) T(std::forward<Args>(args)...);
}
pointer operator ->()
{
return reinterpret_cast<pointer>(mData);
}
private:
alignas(T) int8_t mData[sizeof(T)];
};
这个包装器可以用来包装不应该调用析构函数的类:
struct A
{
int i;
};
static StaticWrapper<A> a;
a->i = 1;
它的工作方式是 - 我们(静态地)保留一些足够大的内存以包含正确对齐的对象。然后我们使用就地new运算符在保留的内存中创建实际对象,并将潜在的参数转发给它的构造函数。我们可以使用operator - &gt;从包装器访问对象。永远不会调用析构函数,因为从编译器的角度来看,T类中没有任何对象 - 只有一个字节数组。我们只是使用这些字节来保存对象。
答案 1 :(得分:1)
在裸机嵌入式系统中,您通常可以访问运行时启动代码(通常在汇编程序中);此代码执行全局静态初始化,包括在调用main()
之前调用构造函数。它还决定了main()
终止时会发生什么;这是静态析构函数将被调用的地方 - 这个代码可以被删除(如果它已经存在),这样就不会在终止时显式调用析构函数 - 这可能允许链接器优化然后删除未使用的代码。
您应该检查映射文件以确定析构函数是否包含在构建中而不是查看编译器汇编程序输出 - 编译器没有选项,只能生成代码,因为它不知道它是否将被外部引用或不。您可能需要设置特定的链接器选项以删除未使用的代码。
答案 2 :(得分:0)
只需使用对堆分配变量的引用。它会泄漏,但我想这就是你想要的。
static A& a = *(new A);
答案 3 :(得分:0)
一个简单的解决方案是使用 placement new - 在适当大小的静态数组上实例化对象。您还可以使用引用变量通过实例而不是指针访问对象。
#include <new>
static char mem_for_a[sizeof(A)] ;
static A* aptr = new(mem_for_a) A ;
static A& a = *aptr ;
static char mem_for_b[sizeof(A)] ;
static A* bptr = new(mem_for_b) A ;
static A& b = *bptr ;
在放置对象中,必须显式调用析构函数,以便完全控制是否以及何时调用它。