【问题标题】:C++11 class with union of string and shared_ptr具有字符串和 shared_ptr 联合的 C++11 类
【发布时间】:2015-02-20 02:01:23
【问题描述】:

(这有点类似于this question,并受到C++11 FAQ on union 的启发,但并非完全如此......)

在用惯用的 C++11 编写类似 Scheme 的解释器的上下文中假设我想要一个字符串、一个 int 和一些闭包的标记联合。所以我可能会编码:

#include <string>
#include <new>
#include <memory>
#include <functional>
enum kind { nothing, string, integer, closure };

class Value {
 enum kind k;
 typedef std::string string_t;
  union {
    std::string str;
    int num;
    std::shared_ptr<std::function<Value(std::vector<Value>)>> clos;
  };
public:
 Value (const Value &v) 
 : k(none) {
   switch (v.k) {
   case none: break;
   case string: new(&str)string_t(v.str); break;
   case integer: num = v.num; break; 
   /// what about closure-s?
   }
   k = v.k;
 };
 Value& operator = (const Value&v) {
   switch (v.k) {
    case none: break;
    case string: new(&str)string_t(v.str); break;
    case integer: num = v.num; break; 
    /// what about closure-s?
   }
  k = v.k;
 }
 /// etc...
};

现在closure 案例呢?对于复制构造函数和赋值运算符,我很想写代码:

 case closure: clos = v.clos; break;

但也许我应该在shared_ptr 上使用一个展示位置new

我不想为此目的使用 Boost(或任何非标准 C++11 库)。

【问题讨论】:

  • 您为什么犹豫将new 用于shared_ptrstring?我不认为将shared_ptr 分配给未初始化的内存是安全的。它可能会尝试破坏一些垃圾位的“托管对象”。哦,在复制赋值运算符中,你应该在重建之前销毁string,否则它会泄漏旧缓冲区。
  • 这可能应该成为一个答案,而不是评论......
  • 如果你不想直接使用 Boost,你至少应该从那里学习如何在boost::variant 中输入安全的方式并使用类似的方法,即访问者模式而不是 switch 等。至少你将检测访问者是否在编译时而不是运行时处理所有变体。
  • @Slava 鉴于问题是关于 C++11 的 unions,我不认为 boost:variant 是一个很好的灵感来源,因为它的很大一部分目的是为了工作围绕在 C++11 放宽之前对 unions 的限制。

标签: c++ c++11 discriminated-union


【解决方案1】:

我看不出有任何理由不将 new 用于 std::shared_ptr,就像您对 std::string 所做的那样。只需分配一个值,如

clos = v.clos;

不安全,因为它会使用可能指向垃圾内存的this 指针调用std::shared_ptr 的复制赋值运算符。它可能会尝试delete 一些不存在的东西。

同样,在您的复制赋值运算符中,您应该在放置新对象之前销毁旧对象,否则旧值会泄漏。

using std::string;
using std::shared_ptr;
if (&v == this)
  return *this;  // self-assignment
switch (this->k)
  {
  case string:
    this->str.~string();
    break;
  case closure:
    this->clos.~shared_ptr();
    break;
  }
this->k = nothing;
// Now we are ready to take the new value.

如果您使用copy & swap idiom,代码可能会更容易维护。由于我没有看到不使用它的任何明显性能提升,我认为在这里不会花费您任何额外费用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 1970-01-01
    • 2020-07-20
    相关资源
    最近更新 更多