首页 > 解决方案 > 删除使数据处于“不良状态”的默认构造函数的模式?

问题描述

假设您有一个类,它的成员应始终设置(即默认值会使它处于不良状态)。为了防止对象具有“不良状态”,您有一个需要设置所有内容的构造函数。问题是,您还有一组“工厂”类,通过解析一些数据流并直接设置成员来创建对象。您希望明确哪些类可以在不使用构造函数的情况下构造您的对象。这有一个好的模式吗?

例子:

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;
};

需要默认构造函数,因为您有工厂类,它们使用“数据”对象获取回调,并基于每个成员填充它的成员。.ie 类似:

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;}
   }
}
);

几个解决方案是:

  1. 将默认构造函数设为私有,添加一个空的朋友类,然后实例化从朋友派生的 CSVParser。
  2. 将 Data 的所有成员移动到一个DataRaw公开所有内容的类,但仅DataRaw用于解析器,并在程序中的其他任何地方使用 Data。
  3. 创建一个DataSerialize派生自Data垃圾并将垃圾传递给构造函数的类,知道它将覆盖它。处理构造的 Data 对象的回调将DataSerialize通过 ref 传递一个对象,并且永远不知道其中的区别。

这似乎也是其他语言中的一个常见问题,其中工厂对象需要能够打破公共/私有边界。

标签: c++design-patternsconstructorfriend

解决方案


如果您真的想明确哪些类可以调用默认构造函数,可以执行以下操作:

#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();
}

但我不认为我会推荐它。


推荐阅读