C ++禁用静态变量的析构函数 [英] C++ disable destructors for static variables

查看:270
本文介绍了C ++禁用静态变量的析构函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个通用类,用于不同的上下文 - 有时作为
静态变量,有时作为堆栈/堆上的正常变量。

I have a general purpose class which is used in different contexts - sometime as static variable, and sometime as a normal variable on the stack/heap.

当它用作正常变量时,析构函数必须在
超出范围时调用 - 正常。可执行文件用在嵌入式目标中,其中
flash是有限的资源,并且永远不会退出,为此我想要
这个退出代码被禁用。

When it is used as a normal variable the destructor must be called when it goes out of scope - as normal. The executable is used in an embedded target where flash is a limited resource and which will never exit, and for this I would like this "exit" code to be disabled.

下面是一个例子来说明这个问题。 A 是正常情况下需要
析构函数的类,但静态
变量不需要
析构函数。

Following is an example to illustrate the problem. A is the class where the destructor is needed for normal circumstances, but is not needed for static variables.

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

Following is the assembler code generated (compiled with -Os -std=c++11 -fno-exceptions -fno-rtti) generated by: 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

如上面的汇编代码所示,向
发出相当数量的指令来执行此清除代码。

As seen in the assembler code above a fair amount of instructions is issued to do this clean up code.

有什么可以做禁用这个不需要的清理代码?它不需要可移植 - 只要它在最新版本的GCC中工作。属性,链接器脚本,更改对象文件和其他技巧是受欢迎的。

Is there anything which can be done to disable this unneeded cleanup code? It does not need to portable - as long as it works in recent versions of GCC. Attributes, linker scripts, altering the object files, and other tricks are mostly welcome.

推荐答案

答案是创建一个包装器:

The answer is by creating a wrapper:

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)];
};

此包装器可用于包装不应调用析构函数的类:

This wrapper can be used to wrap classes which destructor should not be called:

struct A
{
    int i;
};

static StaticWrapper<A> a;
a->i = 1;

它的工作原理是 - 我们保留(静态)一些大到足以包含对象的内存对准。然后我们使用一个就地new运算符在保留的内存中创建实际对象并将可能的参数转发给它的构造函数。我们可以使用operator - >从我们的包装器访问对象。析构函数永远不会被调用,因为从编译器的角度来看,没有类T的任何对象 - 只有一个字节数组。我们只是使用这些字节来保存对象。

The way it works is - we reserve (statically) some memory big enough to contain the object with proper alignment. Then we use an in-place new operator to create the actual object in the reserved memory and forward potential arguments to its constructor. We can access the object from our wrapper using operator ->. The destructor will never be called because, from the compiler perspective, there is no object of class T anywhere - only an array of bytes. We just use those bytes to hold the object.

这篇关于C ++禁用静态变量的析构函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆