2

我有一个包含大量数据表的类,它带有一个构造函数,该构造函数接受计算该数据所需的所有参数。但是,它需要很长时间才能运行,所以我添加了一个构造函数,它接受一个流,并从该流中读取数据。不过,我在想出一种设计此类的 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”来管理。有没有更好的方法来设计这个类?

4

5 回答 5

4

也许是这样的:

DataTable foo = filename.empty()
              ? DataTable(x, y, z)
              : DataTable(std::ifstream(filename));
于 2013-10-04T13:53:29.290 回答
0

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

于 2013-10-04T13:50:30.117 回答
0

一些想法:

  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构造函数中比较好。

于 2013-10-04T13:50:40.777 回答
0

如果该类支持复制,那么 Kerrek SB 的解决方案就是要走的路。然而,从你所说的,复制是昂贵的。在这种情况下,您可以使用 C++11,您可以尝试添加一个移动构造函数,以避免深度复制。否则,您可能会陷入动态分配的困境:

std::auto_ptr<DataTable> fooPtr( filename.empty()
                                 ? new DataTable( x, y z )
                                 : new DataTable( filename ) );
DataTable& foo = *fooPtr;
于 2013-10-04T14:03:14.847 回答
0

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

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;
于 2013-10-04T14:29:48.460 回答