我想避免破坏销毁了对象的对象的开销。 例如,我有一个这样的课程:
#include <iostream>
#include <functional>
class TestClass {
public:
explicit TestClass(std::function<void(void)> f) : f_(std::move(f)) {}
TestClass(const TestClass &) = delete;
TestClass(TestClass &&testobj) {
f_ = testobj.f_;
testobj.f_ = default_f_;
}
~TestClass() { f_(); }
private:
static std::function<void(void)> default_f_;
std::function<void(void)> f_;
};
std::function<void(void)> TestClass::default_f_ = [] { std::cout << "do nothing\n"; };
int main() {
std::function<void(void)> f = [] { std::cout << "do something\n"; };
TestClass t1(f);
TestClass t2(std::move(t1));
}
在这种情况下,我将t1
移到t2
中。我必须创建default_f_
,以便在从t1
移动之后,可以使用它将f_
设置为有效对象,这样销毁就不会引发试图调用空{{ 1}}。
是否有某种方法可以避免使用std::function
,因为这样做似乎效率很低?
编辑:
很多人建议添加支票以避免这种情况。
是的,我在编写的每个析构函数中都这样做了,这只是一个例子。我认为,当发生move构造时,通常认为旧对象是无用的,并且编译器知道这一点,因此即使在析构函数中进行检查也是额外的有效负载,因为我们确定if语句始终是一个在这种情况下一定的价值。
我发现一则帖子也认为析构函数也可能成为瓶颈:https://www.meetingcpp.com/blog/items/c1y-move-copy-destructors.html。这篇文章写在愚人节那天,是个玩笑,但总的想法是我想要的。
正如Peter所指出的,我找到了一篇帖子,谈论这个“破坏性举动”一词:https://foonathan.net/blog/2017/09/14/destructive-move.html。我同意,当发生移动分配时,旧对象是无效对象,不应访问。
答案 0 :(得分:3)
std::function
的实现方式使其在调用operator()
时必须具有有效可调用对象的位置。从std::function
移动时,剩下的是有效但未指定的对象,该对象不再可调用。由于要在析构函数中调用函数,因此有两个选择。您可以将f_
设置为像您所做的那样不执行任何操作
class TestClass {
public:
explicit TestClass(std::function<void(void)> f) : f_(std::move(f)) {}
TestClass(const TestClass &) = delete;
TestClass(TestClass &&testobj) f_(std::move(testobj.f_)) { f_ = [](){}; }
~TestClass() { f_(); }
private:
std::function<void(void)> f_;
};
或者您可以使用if语句解决此问题。您检查_f
是否可调用,并且只有在可以调用时才能在析构函数中调用它。这样您就可以将代码编写为
class TestClass {
public:
explicit TestClass(std::function<void(void)> f) : f_(std::move(f)) {}
TestClass(const TestClass &) = delete;
TestClass(TestClass &&testobj) = default;
~TestClass() { if (f_) f_(); }
private:
std::function<void(void)> f_;
};
这两个选项都将摆脱default_f_
,但是您仍然需要做一些工作以确保您有一个有效的对象,或者在调用f_
之前检查是否有一个有效的对象。