返回值优化和析构函数调用
Return value optimization and destructor calls
我知道RVO主要是应用的,但我能指望它吗?我有一个函数可以创建FlagContainer类的对象。
class FlagContainer {
public:
~FlagContainer() {
someItem->flag = true;
}
private:
Item * someItem;
}
public FlagContainer createFlagContainer() {
return FlagContainer();
}
调用方使用容器后,必须设置标志。所以我可以用析构函数来做这个。
{
FlagContainer container = createFlagContainer();
// do something with container
}
当超出范围时,将调用析构函数。但是我能确定在createFlagContainer中永远不会调用析构函数吗?有什么办法做到这一点吗?
我会使用AVR GCC 4.7.0编译器。
我知道RVO主要是应用的,但我能指望它吗?
不要依赖RVO的逻辑。简单地说,编译程序的人可以通过命令行选项将其关闭。
有什么办法做到这一点吗?
令人惊讶的是,标准库已经为您提供了这一功能,因此您不需要冒自己实现它的风险(众所周知,移动构造函数和运算符很难正确)
带有自定义deleter的std::unique_ptr
可以很好地完成这项工作。
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
using flag_setter = std::unique_ptr<X, flag_setter_impl>;
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x, flag_setter_impl());
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
但我的目标上没有STL
然后不要忘记你的规则0,3,5
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
struct flag_setter
{
flag_setter(X* px) : px(px) {}
flag_setter(const flag_setter&) = delete;
flag_setter(flag_setter&& r) noexcept : px(r.px) { r.px = nullptr; }
flag_setter& operator=(const flag_setter& r) = delete;
flag_setter& operator=(flag_setter&& r)
{
flag_setter tmp(std::move(r));
std::swap(tmp.px, px);
return *this;
}
~flag_setter() noexcept {
flag_setter_impl()(px);
}
private:
X* px;
};
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x);
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
目前还不能保证应用复制省略。有保证的拷贝省略被建议包含在C++17中。是否应用复制省略完全由编译器决定(不过,有些编译器可以选择完全禁用它)。
避免这种需要的一种潜在方法可能是使用一个基本上不可用的类型,该类型只能用作您感兴趣的类型的构造函数参数,并返回该类型的对象:
class FlagContainerBuilder {
friend class FlagContainer;
public:
FlagContainerBuilder(/* suitable arguments go here */);
// nothing goes here
};
class FlagContainer {
// ...
public:
FlagContainer(FlagContainerBuilder&& builder);
// as before
};
FlagContainerBuilder createFlagContainer() { ... }
这样就可以避免可能销毁从createFlagContainer()
返回的FlagContainer
。
我知道RVO主要是应用的,但我能指望它吗?
没有。编译器可以实现RVO,但不是必需的。只有当编译器承诺这样做时,你才能指望它。
尽管根据标准12.8/3/p311.复制和移动类对象[class.copy]将其呈现为编译器可以执行NRVO(也称为复制省略)的上下文,但你不能依赖它。依赖这种优化的程序实际上是不可移植的。
为了确保对象的移动,我将定义一个移动构造函数,在里面我将使另一个对象的指针为空,而在析构函数中,我将检查指针是否为nullptr
,以便将其标志设置为true:
class FlagContainer {
public:
FlagContainer(FlagContainer&& other) : someItem(other.someItem) {
other.someItem = nullptr;
}
~FlagContainer() {
if(someItem) someItem->flag = true;
}
Item * someItem;
};
FlagContainer createFlagContainer() {
return FlagContainer();
}
实时演示
- 什么时候调用析构函数
- C++-明确何时以及如何调用析构函数
- 在c++中使用向量时,如何调用构造函数和析构函数
- C++ 防止在映射中放置()时调用析构函数
- 调用析构函数以释放动态分配的内存
- C++:使用方法调用析构函数的顺序是什么?
- 向量推回调用析构函数时调用析构函数
- 如何在调用析构函数时优雅地停止/销毁带有阻塞调用C++线程?
- C++,我应该调用析构函数吗?
- 如何获取有关在 Clang LibTooling 中调用析构函数的信息?
- 当我从 std::vector 中的新放置调用析构函数时会发生什么?
- 未调用的初始化静态thread_local结构的构造函数和析构函数
- 为什么这里不调用析构函数
- 在调用 std::bind 的产品后意外调用析构函数
- 为什么在传递给函数而不是构造函数时调用析构函数?
- 如何在C++中调用析构函数
- 为什么为未删除的对象调用析构函数?
- 调用析构函数时出错
- C++ 在不释放内存的情况下调用析构函数
- 从函数调用析构函数返回的对象