我需要设计一个结构数据,它将保存指向Base数据类型的指针。用户应该能够轻松地创建此数据结构的对象,并在不处理大量内存管理问题的情况下传递。
我创建了一些结构,请提出正确的处理方法。
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(),我可以避免分段错误,用户可以自由地创建他喜欢的struct Data对象。
有没有更好的方法来设计这些类?
答案 0 :(得分:1)
块1中的代码和块2由于双重删除而崩溃。如何正确处理所有这些用例。
遵循3的规则(如果您想支持有效的移动操作,则遵循5的规则):
如果一个类定义了以下一个(或多个),它应该明确定义所有三个:
- 析
- 复制构造函数
- 复制分配操作员
您忽略了实施自定义复制赋值运算符。使用默认的复制赋值运算符会导致双重删除。
此外,永远不要将指向自动变量的指针指定给Data::baseData
,就像在块1中一样。
Data
的析构函数将删除此指针,从而导致未定义的行为。
此外,永远不要删除Data::baseData
所拥有的指针,除非您要用其他内容替换它。
为避免意外地执行此操作,我建议您已经考虑将Data::baseData
声明为私有。
有没有更好的方法来设计这些类?
是。不要使用裸指针来拥有内存。请改用std::unique_ptr
。
答案 1 :(得分: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) });
}
}
您可以使用与addData