使用std :: unique_ptr和lambdas来提升对象的状态

时间:2013-11-03 06:55:35

标签: c++11 unique-ptr scopeguard

当推进对象的状态时,std::swap的使用适用于简单对象和指针交换。对于其他就地操作,Boost.ScopeExit工作得相当好,但如果您想跨功能共享退出处理程序,那么它并不是非常优雅。是否有一种C ++ 11本机方式来实现类似于Boost.ScopeExit的东西,但允许更好的代码重用?

1 个答案:

答案 0 :(得分:0)

(Ab)使用std::unique_ptr的自定义删除器作为ScopeExitVisitor或后置条件。向下滚动到main()的~7行,看看它在呼叫站点的实际使用情况。以下示例允许std::function / Deleter的{​​{1}}或lambdas不需要任何参数,如果需要将参数传递给{,则允许使用嵌套类{1}} / ScopeExitVisitor

Deleter

产生:

ScopeExitVisitor

从好的方面来说,这个技巧很好,因为:

  • 删除程序中使用的代码可以访问私有变量
  • 删除代码可以集中
  • 仍然可以使用lambdas,但他们只能访问公共成员。
  • 可以通过充当闭包的嵌套类将参数传递给Deleter
  • 并非所有#include <iostream> #include <memory> class A { public: using Type = A; using Ptr = Type*; using ScopeExitVisitorFunc = std::function<void(Ptr)>; using ScopeExitVisitor = std::unique_ptr<Type, ScopeExitVisitorFunc>; // Deleters that can change A's private members. Note: Even though these // are used as std::unique_ptr<> Deleters, these Deleters don't delete // since they are merely visitors and the unique_ptr calling this Deleter // doesn't actually own the object (hence the label ScopeExitVisitor). static void ScopeExitVisitorVar1(Ptr aPtr) { std::cout << "Mutating " << aPtr << ".var1. Before: " << aPtr->var1; ++aPtr->var1; std::cout << ", after: " << aPtr->var1 << "\n"; } // ScopeExitVisitor accessing var2_, a private member. static void ScopeExitVisitorVar2(Ptr aPtr) { std::cout << "Mutating " << aPtr << ".var2. Before: " << aPtr->var2_; ++aPtr->var2_; std::cout << ", after: " << aPtr->var2_ << "\n"; } int var1 = 10; int var2() const { return var2_; } // Forward declare a class used as a closure to forward Deleter parameters class ScopeExitVisitorParamVar2; private: int var2_ = 20; }; // Define ScopeExitVisitor closure. Note: closures nested inside of class A // still have access to private variables contained inside of A. class A::ScopeExitVisitorParamVar2 { public: ScopeExitVisitorParamVar2(int incr) : incr_{incr} {} void operator()(Ptr aPtr) { std::cout << "Mutating " << aPtr << ".var2 by " << incr_ << ". Before: " << aPtr->var2_; aPtr->var2_ += incr_; std::cout << ", after: " << aPtr->var2_ << "\n"; } private: int incr_ = 0; }; // Can also use lambdas, but in this case, you can't access private // variables. // static auto changeStateVar1Handler = [](A::Ptr aPtr) { std::cout << "Mutating " << aPtr << ".var1 " << aPtr->var1 << " before\n"; aPtr->var1 += 2; }; int main() { A a; std::cout << "a: " << &a << "\n"; std::cout << "a.var1: " << a.var1 << "\n"; std::cout << "a.var2: " << a.var2() << "\n"; { // Limit scope of the unique_ptr handlers. The stack is unwound in // reverse order (i.e. Deleter var2 is executed before var1's Deleter). A::ScopeExitVisitor scopeExitVisitorVar1(nullptr, A::ScopeExitVisitorVar1); A::ScopeExitVisitor scopeExitVisitorVar1Lambda(&a, changeStateVar1Handler); A::ScopeExitVisitor scopeExitVisitorVar2(&a, A::ScopeExitVisitorVar2); A::ScopeExitVisitor scopeExitVisitorVar2Param(nullptr, A::ScopeExitVisitorParamVar2(5)); // Based on the control of a function and required set of ScopeExitVisitors that // need to fire use release() or reset() to control which visitors are used. // Imagine unwinding a failed but complex API call. scopeExitVisitorVar1.reset(&a); scopeExitVisitorVar2.release(); // Initialized in ctor. Use release() before reset(). scopeExitVisitorVar2.reset(&a); scopeExitVisitorVar2Param.reset(&a); std::cout << "a.var1: " << a.var1 << "\n"; std::cout << "a.var2: " << a.var2() << "\n"; std::cout << "a.var2: " << a.var2() << "\n"; } std::cout << "a.var1: " << a.var1 << "\n"; std::cout << "a.var2: " << a.var2() << "\n"; } 个实例都需要分配一个对象(例如,将不需要的删除器设置为a: 0x7fff5ebfc280 a.var1: 10 a.var2: 20 a.var1: 10 a.var2: 20 a.var2: 20 Mutating 0x7fff5ebfc280.var2 by 5. Before: 20, after: 25 Mutating 0x7fff5ebfc280.var2. Before: 25, after: 26 Mutating 0x7fff5ebfc280.var1 10 before Mutating 0x7fff5ebfc280.var1. Before: 12, after: 13 a.var1: 13 a.var2: 26 完全可以接受)
  • 在运行时更改行为只需要调用std::unique_ptrnullptr
  • 根据您构建堆栈的方式,当reset()(s)的范围超出范围时,可以在编译时更改对象的安全保证

最后,使用Boost.ScopeExit您可以将调用转发给辅助函数,或者使用类似于release()文档与std::unique_ptr建议的条件。类似于:

Boost.ScopeExit

并且没有任何问题,但就像在原始问题中被问到的那样,如何在不在其他地方代理呼叫的情况下共享代码?将bool commit = ...;的删除者用作#include <iostream> #include <boost/scope_exit.hpp> int main() { bool commitVar1 = false; bool commitVar2 = false; BOOST_SCOPE_EXIT_ALL(&) { if (commitVar1) std::cout << "Committing var1\n" if (commitVar2) std::cout << "Committing var2\n" }; commitVar1 = true; }