Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在两个构造函数之间进行选择的方法_C++_Constructor_Raii - Fatal编程技术网

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调用其中一个函数,或者在一切都失败时抛出异常。

一些想法:

  • 去掉“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...);
    }
    
  • 事实上,现在我想起来了,最好将此逻辑放入
    数据表
    构造函数中。

    一些想法:

  • 去掉“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,再次让构造函数获取右值引用。)