C++ 在两个构造函数之间进行选择的方法
我有一个类,它包含一个大的数据表,有一个构造函数,它获取计算数据所需的所有参数。但是,它需要很长时间才能运行,所以我添加了一个构造函数,它接受一个流,并从该流中读取数据。但是,我在设计这个类时遇到了困难,因为我有两个构造函数,在运行时我需要在它们之间进行选择。这就是我想到的:C++ 在两个构造函数之间进行选择的方法,c++,constructor,raii,C++,Constructor,Raii,我有一个类,它包含一个大的数据表,有一个构造函数,它获取计算数据所需的所有参数。但是,它需要很长时间才能运行,所以我添加了一个构造函数,它接受一个流,并从该流中读取数据。但是,我在设计这个类时遇到了困难,因为我有两个构造函数,在运行时我需要在它们之间进行选择。这就是我想到的: std::string filename; // Populated by command line arguments DataTable table; // Empty constructor, no resource
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”,用于管理对象是默认构造的还是完全初始化的。有更好的方法来设计这个类吗?将决定以何种方式初始化的文件测试代码移到ctor中,将ctor移到两个私有的
init
函数中,从ctor调用其中一个函数,或者在一切都失败时抛出异常。将决定以何种方式初始化的文件测试代码移到ctor中,将ctor移到两个私有的init
函数中,从ctor调用其中一个函数,或者在一切都失败时抛出异常。一些想法:
数据表
:
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...);
}
数据表构造函数中。一些想法:
去掉“inited”标志
如果默认构造函数不能合理地构造对象,请去掉它
使用这种构造获得一个数据表
:
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
构造函数中。可能是这样的:
DataTable foo = filename.empty()
? DataTable(x, y, z)
: DataTable(std::ifstream(filename));
也许是这样:
DataTable foo = filename.empty()
? DataTable(x, y, z)
: DataTable(std::ifstream(filename));
如果类支持复制,那么Kerrek SB的解决方案就是解决方法
外带。然而,据你所说,复制是昂贵的。在里面
在这种情况下,您可以使用C++11,您可以尝试添加一个移动
构造函数,以避免深度复制。否则,你就完了
可能会被卡住:
std::auto_ptr<DataTable> fooPtr( filename.empty()
? new DataTable( x, y z )
: new DataTable( filename ) );
DataTable& foo = *fooPtr;
std::auto_ptr fooPtr(filename.empty())
?新数据表(x,y和z)
:新数据表(文件名));
DataTable&foo=*fooPtr;
如果类支持复制,那么Kerrek SB的解决方案就是这样
外带。然而,据你所说,复制是昂贵的。在里面
在这种情况下,您可以使用C++11,您可以尝试添加一个移动
构造函数,以避免深度复制。否则,你就完了
可能会被卡住:
std::auto_ptr<DataTable> fooPtr( filename.empty()
? new DataTable( x, y z )
: new DataTable( filename ) );
DataTable& foo = *fooPtr;
std::auto_ptr fooPtr(filename.empty())
?新数据表(x,y和z)
:新数据表(文件名));
DataTable&foo=*fooPtr;
为了完整性起见,这里有另一个想法:
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;
模板
未初始化的类
{
公众:
~uninitialized()
{
如果(活着){
算子T&().~T();
}
}
样板
无效创建(Ts&…参数)
{
断言(!alive&&“create只能调用一次”);
void*const p=obj;
::新(p)T(标准::转发(args)…);
活着=真;
}
算子T&()
{
断言(尚未创建活动的&&“T”);
返回*重新解释铸件(obj);
}
私人:
bool-alive=假;
alignas(T)无符号字符对象[sizeof(T)];
};
// ...
std::字符串文件名;
未初始化的表;
if(filename.empty()){
创建(/*各种参数*/);
}否则{
std::ifstream filestream(文件名);
创建(文件流);
}
DataTable&tbl=表;
为了完整性起见,这里有另一个想法:
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;
模板
未初始化的类
{
公众:
~uninitialized()
{
如果(活着){
算子T&().~T();
}
}
样板
无效创建(Ts&…参数)
{
断言(!alive&&“create只能调用一次”);
void*const p=obj;
::新(p)T(标准::转发(args)…);
活着=真;
}
算子T&()
{
断言(尚未创建活动的&&“T”);
返回*重新解释铸件(obj);
}
私人:
bool-alive=假;
alignas(T)无符号字符对象[sizeof(T)];
};
// ...
std::字符串文件名;
未初始化的表;
if(filename.empty()){
创建(/*各种参数*/);
}否则{
std::ifstream filestream(文件名);
创建(文件流);
}
DataTable&tbl=表;
这是正确的方法。如果他这样做的话,他可能会想添加一个移动构造函数,当然,这只有在他有C++11的情况下才会起作用。(他将无法将临时的ifstream
绑定到std::istream&
;他将需要使用类似std::ifstream(filename)的东西。seekg(0,std::ios_base::beg)
。或者让构造函数使用右值引用,如果他有C++11。)这是正确的方法。如果他这样做的话,他可能会想添加一个移动构造函数,当然,这只有在他有C++11的情况下才会起作用。(他将无法将临时的ifstream
绑定到std::istream&
;他需要使用类似std::ifstream(filename)的东西。seekg(0,std::ios_base::beg)
。或者,如果他有C++11,再次让构造函数获取右值引用。)