我有一个包含大型数据表的类,其构造函数包含计算该数据所需的所有参数。但是,运行需要很长时间,因此我添加了一个构造函数来获取流,并从该流中读取数据。虽然因为我有两个构造函数,但是我在设计这个类时遇到了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”标志来管理。有没有更好的方法来设计这个类?
答案 0 :(得分:4)
也许是这样的:
DataTable foo = filename.empty()
? DataTable(x, y, z)
: DataTable(std::ifstream(filename));
答案 1 :(得分:0)
将决定init方式的文件测试代码移动到ctor中,将ctors移动到两个私有init
函数中,从ctor调用其中一个函数或者如果一切都失败则抛出异常。
答案 2 :(得分:0)
一些想法:
使用这种构造来获取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
构造函数中会更好。
答案 3 :(得分:0)
如果班级支持复制,那么Kerrek SB的解决方案就是这样 去。但是,根据你的说法,复制很昂贵。在 那种情况下,您可以使用C ++ 11,您可以尝试添加一个移动 构造函数,以避免深层复制。否则,你就是 可能卡住动态分配:
std::auto_ptr<DataTable> fooPtr( filename.empty()
? new DataTable( x, y z )
: new DataTable( filename ) );
DataTable& foo = *fooPtr;
答案 4 :(得分: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;