删除使数据处于"错误状态"的默认构造函数的模式?
Pattern for removing default constructor which leaves data in "bad state"?
假设你有一个类,它的成员应该总是被设置(即默认值会让它处于错误状态(。为了防止对象具有"错误状态",您有一个构造函数,它要求设置所有内容。问题是,您还有一组"工厂"类,通过解析一些数据流并直接设置成员来创建对象。您希望明确哪些类可以在不使用构造函数的情况下构造对象。这有没有好的模式?
例:
enum class DataState
{
STATE_ONE,
STATE_TWO
};
class Data
{
public:
Data(std::string _dp1, DataState _dp2): dp1(_dp1), dp2(_dp2) {}
Data() {}; /* Don't want this constructor because it forces a default for dp2
std::string dp1;
DataState dp2;
};
需要默认构造函数,因为您有工厂类,这些类使用"Data"对象获取回调,并按成员填充其成员。
Field1Callback(Data &d, const std::string &fieldVal)
{
d.dp1 = field;
}
Field2Callback(Data &d, const std:string &fieldVal)
{
d.dp2 = ConvertToState(fieldVal);
}
是否有任何模式可以明确哪些类可以调用"默认"构造函数? 即序列化程序和解析器?
为了澄清,以下是我正在使用的模板化解析器的一些片段代码:
template<typename R>
class CSVParser
{
public:
CSVParser(std::vector<std::pair<std::string, std::function<bool(const std::string &field, R &rec)>>> parsers);
bool ProcessBuffer(const unsigned char *buffer, size_t length, size_t &bytes_parsed, bool last_buffer, std::function<void(const R& onNewRecord)> onNewRecord)
};
Data
解析器的示例构造可以是:
CSVParser<Data> d(
{
{
"dp1",
[](const std::string &field, Data &rec) { rec.dp1 = field; return true;}
},
{
"dp2",
[](const std::string &field, Data &rec) { rec.dp2 = ConvertToState(field); return true;}
}
}
);
几个解决方案是:
- 将默认构造函数设为私有,添加一个空的友元类,然后实例化派生自友元的 CSVParser。
- 将 Data 的所有成员移动到公开所有内容的类
DataRaw
,但仅将DataRaw
用于解析器,并在程序的其他位置使用 Data。 - 创建一个
DataSerialize
类,该类派生自Data
并将垃圾传递到构造函数中,知道它会覆盖它。处理构造数据对象的回调将通过 ref 传递一个DataSerialize
对象,并且永远不知道其中的区别。
这似乎也是其他语言中的常见问题,其中工厂对象需要能够打破公共/私有边界。
如果确实要明确哪些类可以调用默认构造函数,可以执行以下操作:
#include <memory>
class Factory;
void create_data(Factory &);
class Data {
Data() = default;
friend void create_data(Factory &);
};
class Factory {
std::unique_ptr<Data> _data;
public:
void set_data(std::unique_ptr<Data> &&inp) {
_data = std::move(inp);
}
std::unique_ptr<Data> make_data();
};
void create_data(Factory &f) {
f.set_data(std::unique_ptr<Data>(new Data())); // make_unique does _not_ work.
}
std::unique_ptr<Data> Factory::make_data() {
create_data(*this);
return std::move(_data);
}
int main() {
Factory f;
auto d = f.make_data();
}
但我认为我不会推荐它。
相关文章:
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 初始化具有非默认构造函数的std::数组项的更好方法
- 具有默认模板类型的默认构造函数的类型推导
- 如何使用非默认构造函数实例化模板化类
- 有没有一种代码密度较低的方法来使用非默认构造函数初始化数组?
- 声明没有默认构造函数的字段
- 没有默认构造函数作为模板参数的自定义比较器
- C++17 没有默认构造函数的地图放置(私有默认构造函数)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- C++复制构造函数和默认构造函数
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 为什么即使我调用参数化构造函数也会调用默认构造函数?
- 具有非默认构造函数的单例类
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 如何处理没有默认构造函数但在另一个构造函数中构造的对象?
- 在C++中使用默认构造函数初始化对象的不同方法
- 在没有默认构造函数的情况下创建的派生对象
- 强制使用默认构造函数对成员进行未初始化的声明
- 使用默认构造函数初始化对象的不同方法
- 创建类类型的动态分配数组,其中类不得具有默认构造函数