设计数据类以处理多态数据的正确方法

问题描述:

我需要设计一个结构数据,它将持有指向基本数据类型的指针。用户应该能够轻松创建此数据结构的对象并传递,而无需处理大量的内存管理问题。设计数据类以处理多态数据的正确方法

我已经创建了几个结构,请建议正确的方法来处理它。

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()我能够避免分割故障和用户自由创建结构数据中,曾经他喜欢的对象。

有没有更好的方法来设计这些类?

你的问题是你正试图自己管理所有权。相反,您可以使用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和块2中的代码由于双重删除而崩溃。我如何正确处理所有这些用例。

通过以下3规则(或5的规则,如果你想支持高效的移动操作):

如果一个类定义的下列它可能应该明确一个(或多个)定义所有三:

  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值运算符

您已经忽略了实现自定义复制赋值操作符。使用默认的复制赋值运算符会导致双重删除。


而且,从来没有像你在块1

Data的析构函数将删除此指针,这会导致不确定的行为在这里做一个指针赋给一个自动变量Data::baseData

此外,除非你打算用其他东西替换它,否则永远不要删除Data::baseData所拥有的指针。

为了避免意外执行这些操作,我建议您声明Data::baseData专用,因为您已经考虑过了。


有没有什么更好的方法来设计这些类?

是的。永远不要使用裸指针来拥有内存。改为使用std::unique_ptr