【问题标题】:RAII way to choose between two constructors在两个构造函数之间进行选择的 RAII 方式
【发布时间】:2013-10-04 13:44:35
【问题描述】:

我有一个类,其中包含一个大型数据表,其构造函数采用计算该数据所需的所有参数。但是,它需要很长时间才能运行,所以我添加了一个构造函数,它接受一个流,并从该流中读取数据。不过,我在想出设计此类的 RAII 方法时遇到了麻烦,因为我有两个构造函数,并且在运行时我需要在它们之间进行选择。这是我想出的:

std::string filename; // Populated by command line arguments
DataTable table; // Empty constructor, no resource acquisition or initialization

if( filename.empty() ) {
    table = DataTable(/*various parameters*/);
} else {
    std::ifstream filestream(filename);

    table = DataTable(filestream); // Reads from file
}

这对我来说看起来很脆弱。默认构造函数将使对象处于有效状态,但无用。它的唯一用途是在外部范围内创建一个“临时”对象,以分配给 if 语句的一个分支。此外,如果对象是默认构造的还是完全初始化的,在幕后还有一个标志“inited”来管理。有没有更好的方法来设计这个类?

【问题讨论】:

    标签: c++ constructor raii


    【解决方案1】:

    可能是这样的:

    DataTable foo = filename.empty()
                  ? DataTable(x, y, z)
                  : DataTable(std::ifstream(filename));
    

    【讨论】:

    • 这是正确的方法。如果他这样做了,他可能会想要添加一个移动构造函数,当然,这只有在他有 C++11 时才有效。 (而且他将无法将临时的ifstream 绑定到std::istream&;他需要使用std::ifstream(filename).seekg( 0, std::ios_base::beg ) 之类的东西。或者如果他有C++,让构造函数再次获取右值引用11.)
    【解决方案2】:

    将决定初始化方式的文件测试代码移动到 ctor 中,将 ctor 移动到两个私有 init 函数中,从 ctor 中调用其中一个函数,或者如果一切都失败则抛出异常。

    【讨论】:

      【解决方案3】:

      一些想法:

      1. 去掉“inited”标志。
      2. 如果默认构造函数不能合理地构造对象,就去掉它
      3. 使用这种构造来获取DataTable:

        DataTable get_me_my_data_fool(ParameterTypes... params, const string& filename = "")
        {
          if(!filename.empty())
            return DataTable(std::ifstream(filename)); // check if file exists!
          else  
            return DataTable(params...);
        }
        

      其实现在想来,还是把这个逻辑放到DataTable构造函数里面比较好。

      【讨论】:

        【解决方案4】:

        如果类支持复制,那么 Kerrek SB 的解决方案就是方法 去。然而,从你所说的,复制是昂贵的。在 在这种情况下,您可以使用 C++11,您可以尝试添加移动 构造函数,以避免深拷贝。否则你就是 可能卡住了动态分配:

        std::auto_ptr<DataTable> fooPtr( filename.empty()
                                         ? new DataTable( x, y z )
                                         : new DataTable( filename ) );
        DataTable& foo = *fooPtr;
        

        【讨论】:

          【解决方案5】:

          为了完整起见,这是另一个想法:

          template<typename T>
          class uninitialised
          {
          public:
              ~uninitialised()
              {
                  if (alive_) {
                      operator T&().~T();
                  }
              }
          
              template<typename... Ts>
              void create(Ts&&... args)
              {
                  assert(!alive_ && "create must only be called once");
                  void* const p = obj_;
                  ::new(p) T(std::forward<Ts>(args)...);
                  alive_ = true;
              }
          
              operator T&()
              {
                  assert(alive_ && "T has not been created yet");
                  return *reinterpret_cast<T*>(obj_);
              }
          
          private:
              bool alive_ = false;
              alignas(T) unsigned char obj_[sizeof(T)];
          };
          
          // ...
          
          std::string filename;
          uninitialised<DataTable> table;
          
          if (filename.empty()) {
              table.create(/* various parameters */);
          } else {
              std::ifstream filestream(filename);
              table.create(filestream);
          }
          
          DataTable& tbl = table;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-10-11
            • 2014-10-19
            • 2013-02-23
            • 2016-12-23
            • 2020-05-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多