绑定到枚举的类成员 我可能面临一些C++类设计的问题,主要是因为我需要把数据库集成到我的系统中。我有一个基类,它有一些子类。我发现有一个名为type的成员是多余的,它是一个枚举,描述了它的子类type。范例 enum FooTypes { kFooTypeGeneric, kFooTypeA, kFooTypeB }; class Foo { public: Foo(FooTypes type):type(type){} FooTypes type; }; class FooA : public Foo { public: FooA():Foo(kFooTypeA){} }; class FooB : public Foo { public: FooB() :Foo(kFooTypeB) {} };
我觉得必须维护枚举的原因是绑定到枚举的类成员 我可能面临一些C++类设计的问题,主要是因为我需要把数据库集成到我的系统中。我有一个基类,它有一些子类。我发现有一个名为type的成员是多余的,它是一个枚举,描述了它的子类type。范例 enum FooTypes { kFooTypeGeneric, kFooTypeA, kFooTypeB }; class Foo { public: Foo(FooTypes type):type(type){} FooTypes type; }; class FooA : public Foo { public: FooA():Foo(kFooTypeA){} }; class FooB : public Foo { public: FooB() :Foo(kFooTypeB) {} };,c++,enums,C++,Enums,我觉得必须维护枚举的原因是Foos的所有者希望将他们创建的内容存储到数据库表中。如果系统重新启动,他们应该能够查看自己的表并说,“哦,是的,我有一个FooA需要初始化”,在这种情况下,只有将名为“FooType”的列设置为1,才能真正做到这一点 我只是想知道,这种给子类一个类型的方法是否是一种糟糕的设计,这种类型是枚举的一部分,他们必须知道。这似乎是多余的。这很好。通常,您希望避免“了解”多态层次结构中的类型-如果您对虚拟分派的性能满意,那么这应该是一个好的设计所需要的全部 但有时您确实需要一个
Foo
s的所有者希望将他们创建的内容存储到数据库表中。如果系统重新启动,他们应该能够查看自己的表并说,“哦,是的,我有一个FooA
需要初始化”,在这种情况下,只有将名为“FooType”的列设置为1,才能真正做到这一点
我只是想知道,这种给子类一个类型的方法是否是一种糟糕的设计,这种类型是枚举的一部分,他们必须知道。这似乎是多余的。这很好。通常,您希望避免“了解”多态层次结构中的类型-如果您对虚拟分派的性能满意,那么这应该是一个好的设计所需要的全部 但有时您确实需要一个强大的映射(例如,用于传递到某个外部资源),让一个枚举标识实例的实际“类型”可以节省一个字符串
dynamic\u cast
来完成相同的工作
您可以兼收并蓄,创建一个
virtual
函数,为类返回正确的枚举。但是坦率地说,您是在白白牺牲性能。您可以使用多态性并对派生类使用重写流函数。
然后需要一个工厂函数来创建不同的派生对象,具体取决于从数据库中读取的内容
下面是一个小示例,其中数据库是一个std::istringstream
,保存以前保存的内容
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
// An abstract base class for all entities you can store in the database
struct Foo {
virtual ~Foo() = default;
virtual void serialize(std::ostream&) const = 0; // must be overridden
virtual void deserialize(std::istream&) = 0; // must be overridden
};
// streaming proxies, calling the overridden serialize/deserialize member functions
std::ostream& operator<<(std::ostream& os, const Foo& f) {
f.serialize(os);
return os;
}
std::istream& operator>>(std::istream& is, Foo& f) {
f.deserialize(is);
return is;
}
//--------------------------------------------------------------------------------------
class FooA : public Foo {
public:
void serialize(std::ostream& os) const override {
// serializing by streaming its name and its properties
os << "FooA\n" << a << ' ' << b << ' ' << c << '\n';
}
void deserialize(std::istream& is) override {
// deserializing by reading its properties
if(std::string line; std::getline(is, line)) {
std::istringstream iss(line);
if(not (iss >> a >> b >> c)) is.setstate(std::ios::failbit);
}
}
private:
int a, b, c;
};
class FooB : public Foo {
public:
void serialize(std::ostream& os) const override {
os << "FooB\n" << str << '\n';
}
void deserialize(std::istream& is) override {
std::getline(is, str);
}
private:
std::string str;
};
//--------------------------------------------------------------------------------------
// A factory function to create derived objects from a string by looking it up in a map
// and calling the mapped functor.
//--------------------------------------------------------------------------------------
std::unique_ptr<Foo> make_foo(const std::string& type) {
static const std::unordered_map<std::string, std::unique_ptr<Foo>(*)()> fm = {
{"FooA", []() -> std::unique_ptr<Foo> { return std::make_unique<FooA>(); }},
{"FooB", []() -> std::unique_ptr<Foo> { return std::make_unique<FooB>(); }},
};
if(auto it = fm.find(type); it != fm.end()) return it->second(); // call the functor
throw std::runtime_error(type + ": unknown type");
}
//--------------------------------------------------------------------------------------
// Deserialize all Foos from a stream
//--------------------------------------------------------------------------------------
std::vector<std::unique_ptr<Foo>> read_foos(std::istream& is) {
std::vector<std::unique_ptr<Foo>> entities;
std::string type;
while(std::getline(is, type)) { // type is for example "FooA" here
// Call make_foo(type), put the result in the vector and
// stream directly to the added element (C++17 or later required)
if(not (is >> *entities.emplace_back(make_foo(type)))) {
throw std::runtime_error(type + ": deserializing failure");
}
}
return entities;
}
//--------------------------------------------------------------------------------------
int main() {
std::istringstream db(
"FooA\n"
"1 2 3\n"
"FooB\n"
"Hello world\n"
);
auto entities = read_foos(db);
std::cout << "serialize what we got:\n";
for(auto& fooptr : entities) {
std::cout << *fooptr;
}
}
我假设
FooA
和FooB
有不同的成员变量?当您从数据库中读取数据时,您今天是如何处理这些数据的?是的,它们将有不同的成员和方法。但我明白你的意思。。如果它们是从数据库动态创建的,那么它将不能使用除基成员的方法之外的任何其他方法。。可能是多态性和不同派生类的不同流函数?您可以有一个工厂来创建对象,然后流式传输到它们?听起来可以通过使用virtual std::string serialize()来修复它代码>和虚拟空反序列化(std::string)代码>将有效地将C++对象转换为/来自数据库表示。子类将重写此方法以提供自己的序列化机制。在这种情况下,为什么要使用virtual
?基类只能有一个getType()
函数?@Surt正好。。。另一种选择是在每个派生类中实现一个virtual
,它只直接返回“正确”的枚举,但正如我所说的,与这个漂亮、干净、便宜的解决方案相比,这真是太过分了。
serialize what we got:
FooA
1 2 3
FooB
Hello world