C++禁用静态变量的析构函数
C++ disable destructors for static variables
我有一个通用类,用于不同的上下文 - 有时为静态变量,有时作为堆栈/堆上的普通变量。
当它用作普通变量时,析构函数必须在它消失时调用超出范围 - 正常。可执行文件用于嵌入式目标,其中闪存是一种有限的资源,永远不会退出,为此我想要此"退出"代码将被禁用。
下面是一个示例来说明该问题。 A
是其中析构函数在正常情况下是必需的,但在静态情况下不需要变量。
struct Abstract {
virtual ~Abstract() {}
};
struct A : public Abstract {
int i = 0;
};
static A a;
static A b;
以下是生成的汇编代码(使用 -Os -std=c++11
-fno-exceptions -fno-rtti
编译)生成者:http://goo.gl/FWcmlu
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 中工作。属性、链接器脚本、更改对象文件和其他技巧大多是受欢迎的。
答案是通过创建一个包装器:
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 运算符在保留内存中创建实际对象,并将潜在的参数转发到其构造函数。我们可以使用运算符 -> 从包装器访问对象。析构函数永远不会被调用,因为从编译器的角度来看,任何地方都没有类 T 的对象 - 只有一个字节数组。我们只是使用这些字节来保存对象。
只需使用对堆分配变量的引用。 它会泄漏,但我想这就是你想要的。
static A& a = *(new A);
在裸机嵌入式系统中,您通常可以访问运行时启动代码(通常在汇编程序中);此代码执行全局静态初始化,包括在调用main()
之前调用构造函数。 它还确定如果main()
终止会发生什么;这就是调用静态析构函数的地方 - 可以删除此代码(如果它已经存在),以便在终止时不会显式调用析构函数 - 这可能允许链接器优化然后删除未使用的代码。
您应该检查映射文件以确定析构函数是否包含在构建中,而不是查看编译器汇编器输出 - 编译器别无选择,只能生成代码,因为它不知道它是否会被外部引用。 可能需要设置特定的链接器选项来删除未使用的代码。
一个简单的解决方案是使用 place 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 ;
在放置对象中,必须显式调用析构函数,以便完全控制是否调用析构函数以及何时调用析构函数。
- 在Visual Studio中,与std::async一起使用时不调用"thread_local"变量"析构函数,这是一个错误吗?
- 使用线程销毁类析构函数中的shared_ptr成员变量
- 为什么不调用全局静态变量的析构函数
- cpp 中的析构函数是否自动调用?即使析构函数没有提及非动态变量,它们也会被删除吗?
- 返回堆栈变量时停止调用析构函数
- 如何使用非平凡析构函数防止未使用的变量警告
- 如何释放C++异常类析构函数中的变量
- 为什么msvc编译器在显式调用析构函数时会发出未使用的变量
- 当变量仍然存在时调用C++析构函数
- 为静态分配的变量手动调用析构函数
- 人造人.NDK.如何记录全局变量的调用析构函数
- 在析构函数调用后访问成员变量
- 为什么析构函数不修改返回的变量
- 从静态变量引用非静态变量会使非静态变量先于静态变量进行析构函数
- 在C++中,析构函数的调用顺序和成员变量的销毁顺序是什么
- 对变量范围感到困惑 - 析构函数意外调用
- 您是否需要析构函数来销毁全局 int、float 或 std::string 变量
- 全局变量析构函数中的 ctrl-c 上的 AV
- C++禁用静态变量的析构函数
- 析构函数在(堆栈)变量之间的赋值时调用