2017-10-04 52 views
-1

我需要設計一個結構數據,它將持有指向基本數據類型的指針。用戶應該能夠輕鬆創建此數據結構的對象並傳遞,而無需處理大量的內存管理問題。設計數據類以處理多態數據的正確方法

我已經創建了幾個結構,請建議正確的方法來處理它。

struct BaseData { 
    enum DataType { DATATYPE_1, DATATYPE_2 }; 
    virtual ~BaseData() { cout << "BaseData Dtor" << endl; } 
}; 

struct DataType1 : BaseData { 
    virtual ~DataType1() { cout << "DataType1 Dtor" << endl; } 
}; 

struct DataType2 : BaseData { 
    virtual ~DataType2() { cout << "DataType2 Dtor" << endl; } 
}; 

struct Data { 
    Data() { cout << "Data Ctor" << endl; } 
    Data(const Data& o) { 
     if (o.baseData->type == BaseData::DATATYPE_1) { 
     baseData = new DataType1; 
     *(static_cast<DataType1*>(baseData)) = *(static_cast<DataType1*>(o.baseData)); 
     } 
     else if (o.baseData->type == BaseData::DATATYPE_2) { 
     baseData = new DataType2; 
     *(static_cast<DataType2*>(baseData)) = *(static_cast<DataType2*>(o.baseData)); 
     } 
    } 
    virtual ~Data() { 
     cout << "Data Dtor" << endl; 
     delete baseData; //here it results in segmentation fault if object is created on stack. 
     baseData = NULL; 
    } 

    BaseData* baseData; 
}; 

vector <Data> vData; 
void addData(const Data& d) { cout << "addData" << endl; vData.push_back(d); } 

客戶端代碼如下所示。

int main() 
{ 
    { 
     DataType1 d1; 
     d1.type = BaseData::DATATYPE_1; 
     Data data; 
     data.baseData = &d1;  
     addData(data); 
    } 

    { 
     BaseData* d2 = new DataType2; 
     d2->type = BaseData::DATATYPE_2; 
     Data data; 
     data.baseData = d2; 
     addData(data); 
     delete d2; 
     d2 = NULL; 
    } 

    { 
     Data data; 
     data.baseData = new DataType1; 
     static_cast<DataType1*>(data.baseData)->type = BaseData::DATATYPE_1; 
     addData(data); 
     delete data.baseData; 
     data.baseData = NULL; 
    } 
} 

塊1和塊2中的代碼由於雙重刪除而崩潰。我如何正確處理所有這些用例。

我想到的一種方法是,使用private隱藏baseData指針並向用戶setBaseData(const BaseData& o)提供方法struct Data

void setBaseData(const BaseData& o) { 
    cout << "setBaseData" << endl; 
    if (o.type == BaseData::DATATYPE_1) { 
     baseData = new DataType1; 
     *(static_cast<DataType1*>(baseData)) = static_cast<const DataType1&>(o); 
    } 
    else if (o.type == BaseData::DATATYPE_2) { 
     baseData = new DataType2; 
     *(static_cast<DataType2*>(baseData)) = static_cast<const DataType2&>(o); 
    } 
} 

隨着setBaseData()我能夠避免分割故障和用戶自由創建結構數據中,曾經他喜歡的對象。

有沒有更好的方法來設計這些類?

回答

1

你的問題是你正試圖自己管理所有權。相反,您可以使用unique_ptr類型的顯式所有權管理。

假設你所使用的相同類型定義(+的createDataType方法,我們將在後面看到):

struct BaseData { 
    enum DataType { DATATYPE_1, DATATYPE_2 }; 
    virtual ~BaseData() { cout << "BaseData" << endl; } 

    static std::unique_ptr<BaseData> createDataType(DataType type); 
}; 

struct DataType1 : BaseData { 
    virtual ~DataType1() { cout << "DataType1" << endl; } 
}; 

struct DataType2 : BaseData { 
    virtual ~DataType2() { cout << "DataType2" << endl; } 
}; 

請注意,我們現在使用的是工廠創建我們的對象,像這樣:

static std::unique_ptr<BaseData> BaseData::createDataType(BaseData::DataType type) { 
    switch(type) { 
    case BaseData::DATATYPE_1: 
     return std::make_unique<DataType1>(); 
    case BaseData::DATATYPE_2: 
     return std::make_unique<DataType2>(); 
    default: 
     throw std::runtime_error("ERR"); 
    } 
} 

然後,你應該申報的管理Data對象如下:

struct Data { 
    Data() 
    : baseData(nullptr) {} 
    Data(std::unique_ptr<BaseData> data) 
    : baseData(std::move(data)) {} 
    Data(Data && rhs) 
    : baseData(std::move(rhs.baseData)) {} 

    std::unique_ptr<BaseData> baseData; 
}; 

現在我們可以寫乾淨,清晰,安全的代碼,因爲這:

vector<Data> vData; 
void addData(Data&& d) { 
    if (dynamic_cast<DataType1 *>(d.baseData.get()) != nullptr) 
    cout << "Adding DataType 1" << endl; 
    else if (dynamic_cast<DataType2 *>(d.baseData.get()) != nullptr) 
    cout << "Adding DataType 2" << endl; 

    vData.push_back(std::move(d)); 
} 

int main() 
{ 
    { // Option 1: Create base data somewhere, create data from it 
     auto baseData = createDataType(BaseData::DATATYPE_1); 
     Data data { std::move(baseData) }; 
     addData(std::move(data)); 
    } 

    { // Option 2: Create data directly knowing the base data type 
     Data data { createDataType(BaseData::DATATYPE_2) }; 
     addData(std::move(data)); 
    } 

    { // Option 3: Create data and add it to the vector 
     addData({ createDataType(BaseData::DATATYPE_1) }); 
    } 
} 

而且你總是可以檢查使用相同的動態的baseData的實際類型轉換爲addData

1

塊1和塊2中的代碼由於雙重刪除而崩潰。我如何正確處理所有這些用例。

通過以下3規則(或5的規則,如果你想支持高效的移動操作):

如果一個類定義的下列它可能應該明確一個(或多個)定義所有三:

  • 析構函數
  • 拷貝構造函數
  • 拷貝賦值運算符

您已經忽略了實現自定義複製賦值操作符。使用默認的複製賦值運算符會導致雙重刪除。


而且,從來沒有像你在塊1

Data的析構函數將刪除此指針,這會導致不確定的行爲在這裏做一個指針賦給一個自動變量Data::baseData

此外,除非你打算用其他東西替換它,否則永遠不要刪除Data::baseData所擁有的指針。

爲了避免意外執行這些操作,我建議您聲明Data::baseData專用,因爲您已經考慮過了。


有沒有什麼更好的方法來設計這些類?

是的。永遠不要使用裸指針來擁有內存。改爲使用std::unique_ptr