2013-10-04 2 views
2

데이터를 계산하는 데 필요한 모든 매개 변수를 사용하는 생성자와 함께 큰 데이터 테이블을 보유하는 클래스가 있습니다. 그러나 실행하는 데 시간이 오래 걸리므로 스트림을 가져 와서 해당 스트림에서 데이터를 읽는 생성자를 추가했습니다. 나는 두 개의 생성자가 있기 때문에이 클래스를 디자인하는 RAII 방식을 고안하는 데 어려움을 겪고있다. 그리고 런타임에 나는 그들 사이에서 선택해야한다. 이것은 제가 생각해 낸 것입니다 :두 생성자 중 하나를 선택하는 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

:

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

합니다. 그는 아마도 그렇게한다면 move 생성자를 추가하기를 원할 것입니다. 물론 C++ 11이있는 경우에만 작동합니다. (그리고 그는 임시'ifstream'을'std :: istream &'에 바인딩 할 수 없으며,'std :: ifstream (filename) .seekg (0, std :: ios_base : : beg)'. 또는 C++ 11 인 경우 생성자에 대한 rvalue 참조를 다시 가져옵니다. –

0

초기화 방법을 결정하는 파일 테스트 코드를 ctor로 옮기고 ctor를 두 개의 전용 init 함수로 옮기고 ctor에서 이들 중 하나를 호출하거나 모든 것이 실패 할 경우 예외를 throw하십시오.

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...); 
    } 
    
  4. 사실

, 지금은 대한 생각 이 로직을 DataTable 생성자에 넣는 것이 낫습니다. 어쩌면이 같은

0

클래스가 지원하는 복사하는 경우는, 다음 Kerrek SB의 솔루션은 이동하는 방법 입니다. 그러나 당신이 말하는 것에서, 복사는 비쌉니다. 이 경우 그 경우에는 C++ 11을 사용할 수 있습니다. 복사를하지 않으려면 생성자를 추가해보십시오. 그렇지 않으면, 당신은 아마 동적으로 할당 붙어있어 :

std::auto_ptr<DataTable> fooPtr(filename.empty() 
           ? new DataTable(x, y z) 
           : new DataTable(filename)); 
DataTable& foo = *fooPtr; 
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;