C ++:优化析构函数调用

时间:2017-06-16 20:53:44

标签: c++ c++11

这里有一些代码示例:

struct Data {
};

struct Init {
    Data *m_data;

    Init() : m_data(new Data) { }
    ~Init() {
        delete m_data;
    }
};

class Object {
    private:
        int m_initType;
        Data *m_data;
    public:
        Object(const Init &init) : m_initType(0), m_data(init.m_data) { }
        Object(Init &&init) : m_initType(1), m_data(init.m_data) { init.m_data = nullptr; }
        ~Object() {
            if (m_initType==1) {
                delete m_data;
            }
        }
};

对象可以通过两种方式初始化:

  • const Init& :这个初始化只是将m_data存储为指针,m_data 不是拥有,所以~Object()不需要做任何事情(在这种情况下,m_data将在~Init())
  • 中销毁
  • 初始&& :此初始化转移m_data的所有权,对象成为m_data的所有者,因此~Object()需要销毁它

现在,有一个功能:

void somefunction(Object object);

在callInitA和callInitB中调用此函数:

void callInitA() {
    Init x;
    somefunction(x); // calls the "const Init &" constructor
}

void callInitB() {
    somefunction(Init()); // calls the "Init &&" constructor
}

现在,这就是我想要完成的事情:在callInitA的情况下,我想让编译器优化掉生成的临时 Object <的析构函数调用/ strong>(对象经常使用,我希望减少代码大小。)

然而,编译器并没有对其进行优化(使用GCC和clang测试)。

对象的设计是因为它没有任何改变m_initType的函数,所以编译器能够发现如果m_initType在构造时设置为0,那么它赢了& #39; t改变,所以在析构函数中它仍然是0 - &gt;根本不需要调用析构函数,因为它什么都不做。

甚至,m_initType是对象的不必要成员:它只在破坏时需要。

你有任何设计理念如何实现这个目标吗?

更新:我的意思是使用某种c ++构造(辅助类等)。 C ++是一种强大的语言,可能有某种c ++技巧可以做到这一点。

(我的原始问题更复杂,这个简化的一个:对象可以用其他类型的Init结构初始化,但所有对象构造函数归结为得到一个&#34;数据*&#34;不知何故)

2 个答案:

答案 0 :(得分:2)

void callInitA() {
    Init x;
    somefunction(x); // calls the "const Init &" constructor
}

无论x的内容如何,​​都无法优化Init的破坏。这样做会违反语言的设计。

这不仅仅是Init是否包含资源的问题。与所有对象一样,Init x将在堆栈上分配以后需要清理的空间,作为析构函数的隐式(不是您自己编写的代码的一部分)。这是不可避免的。

如果x的目的是成为somefunction无需重复创建和删除对x的引用即可调用的对象,您应该像这样处理它:

void callInitA(Init & x) { //Or Init const& x
    somefunction(x); // calls the "const Init &" constructor
}

其他几点说明:

  • 确保在拥有资源的任何对象上实施Rule of Five(有时称为三个规则)。
  • 您可以考虑将所有指针包装在std::unique_ptr内,因为您似乎不需要超出std::unique_ptr提供的功能。

答案 1 :(得分:0)

你的m_initType实际上区分了两种Object - 那些拥有记忆的人和那些没有记忆的人。另外,你提到实际上有很多种Object可以用各种输入进行初始化;所以实际上有各种Object s。这表明Object应该更好地成为一些抽象基类。现在,这不会加速任何事情或避免析构函数调用,但它可能会使您的设计更合理。或者Object可能是std::variant(C ++ 17中的新内容,您可以阅读它)。

但是,你说临时Object是“频繁使用”。所以也许你应该采取另一种方式:在你的例子中,假设你有

template <bool Owning> class Object;

然后你将专门针对非拥有案例,只有一个const Init&amp;构造函数和默认销毁,以及拥有案例,只有一个Init&amp;&amp;构造函数(考虑到你提到的两个)和delete s的析构函数。这意味着对使用Object的代码进行模板化,许多意味着更大的代码大小,以及必须知道传入的Object种类;但如果能避免这种情况,请检查是否真的让你非常烦恼。

  

我想减少代码大小

我有点怀疑你这样做。你在为嵌入式系统编写代码吗?在这种情况下,使用大量临时Object时会有点奇怪。