通过从文本文件中读取对象的名称来构造对象
Construct an object by reading it's name from text file
我有一个名为shapes的基类,其派生类为3D形状,即Ball或Tetraeder。我的程序应该从文本文件中读取形状的类型和参数,并将体积和面积写入输出文本。
#include <fstream>
#include <string>
#include <sstream>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include "Shape.h"
#include "Ball.h"
#include <vector>
using namespace std;
int
main( int argc, char** argv )
{
string input = string(argv[1]);
string output = string(argv[2]);
ifstream file(input);
string line;
string shapename;
int nx = atoi(argv[3]);
int ny = atoi(argv[4]);
int nz = atoi(argv[5]);
while (std::getline(file, line))
{
std::stringstream lineStream(line);
lineStream >> shapename;
int value;
std::vector<int> lineData;
while (lineStream >> value)
{
lineData.push_back(value);
}
Shape * objShape = new shapename(lineData);
objShape -> calc_volume;
objShape -> calc_projection(nx,ny,nz);
std::ofstream f(output);
f << objShape -> get_volume() << " " << objShape -> get_projection << endl;
}
}
我的问题是我现在如何从一个文本文件中的字符串创建一个对象,特别是不知道所有的派生类。
应该可以在不更改代码的情况下向程序中添加更多形状,只需添加新文件。
问题是:
我的问题是我现在如何创建一个对象从字符串在
答案是:你必须知道所有的派生类。
c++没有反射。因此,所有的类名在编译时都是绑定的,这种工厂别无选择,只能做以下的一些变化:
if (name == "box")
return new Box();
else if (name == "circle")
return new Circle();
// ... etc ... etc ...
有各种不同的方法和设计模式可以自动化一些繁重的工作,并使其足够灵活,以避免必须显式维护所有子类的硬编码列表。
我将概述一个简短的,非常简短的方法。我以前使用过一个非常简单的方法,并且实现了几乎相同的结果:一个可以按名称实例化给定子类的工厂,在这种方式下,您不必手动编辑工厂,并添加更多的代码行。为新子类创建工厂的整个过程可以整齐地包装到创建新子类的过程中,使其成为一个相当可靠的、划分的解决方案。
考虑为这些子类注册工厂的简单机制:
typedef Shape (*shape_factory_t)();
Shape
是你的形状的超类。
工厂的工作方式是这样的:
std::map<std::string, shape_factory_t> all_factories;
void register_factory(const std::string &name, shape_factory_t factory)
{
all_factories[name]=factory;
}
现在你有一张所有工厂的地图。除了无穷无尽的if
语句,您有一个单独的映射,您可以通过类名查找它,并调用适当的工厂,如:
auto iter=all_factories.find(name);
if (iter == all_factories.end())
throw; // Some exception, unknown subclass
return (*iter->second)();
好了,这部分已经解决了。现在的问题是:如何为每个子类注册一个工厂。
假设你有一个Circle的实现:
class Circle : public Shape {
class initializer;
// ... other things that make up the Circle
};
然后,在circle.cpp
中,实现了这个子类:
static Shape *create_circle()
{
return new Circle(); // Add constructor parameters, as appropriate
}
class Circle::initializer {
public:
initializer() {
register_factory("circle", create_circle);
}
};
static initializer initialize_me;
以这种方式,Circle
类通过类名将自己注册到创建给定Shape
实例的工厂。您可以继续并单独实现所有其他子类,而无需触及主工厂代码。你可以用同样的方式声明你的Box
子类,并让它向工厂注册自己,然后工厂将自动知道创建一个Box
类(大概是通过调用create_box
()函数),给定名称为"box"。
还有一个需要注意的细节:初始化顺序。如你所知,在不同的翻译单元中,全局作用域对象的相对初始化顺序是由实现定义的,而在c++中则没有指定。
所有工厂函数的全局std::map
必须在所有子类尝试注册它们自己之前构造,并在应用程序启动时将它们自己放入映射中。
这是一个相当典型的静态初始化顺序失败问题,有几种已知的解决方案。
c++就没有那么灵活了。添加新形状意味着添加新类(因为您已经创建了一个shapes、一个Ball和一个Tetraeder类,我假设您想创建更多的类)。如果你添加新的类,你将不得不修改代码,这意味着你必须重新编译。
有来知道派生类是什么。你是编码它们的人,所以你最好也有一份清单。要使程序灵活,最好的办法就是使用头文件,你似乎已经在这么做了。
至于从文本文件中的字符串创建对象(虽然您知道3D对象类是什么),您可以解析字符串,读取它想要制作的形状,然后做一些相当简单的事情,例如:
//shapeType - a string containing the type of the 3D object
Shape *newShape;
switch(shapeType) {
case "ball":
newShape = new Ball(...); // ... - parameters for the ball dimensions
break;
case "tetraeder":
newShape = new Tetraeder(...); // ... - parameters again
break;
default:
return -1;
}
//and now you can use newShape as you wish
- 检查哪个对象调用了另一个对象的对象方法
- 如何从传递到对象的对象内部访问对象的类属性?
- C++,如何根据运行时条件构造引用不同 istream 对象的对象?
- 如何在C++中为增加但记住删除先前对象的对象分配唯一标识符
- 对象内部对象的重载功能
- 返回一个引用C++中另一个类对象的对象的有效方法
- 当对象被删除时,复制被删除对象的对象如何
- 在递归函数中更有效地创建对象和对象数组?C++
- 初始化多对象双对象<矢量<矢量<float>> >
- 基准、值、值类型、对象和对象类型(C++)
- 如何仅通过父类对象限制对象创建
- 由另一个对象重用对象的空间
- 在另一个对象 B 中创建对象 A 时,对象 A 是否是对象 B 的本地对象,对象 A 是否会存在于对象 B 的实例化之外?
- 为什么当我在构造函数中创建线程时,实例化对象和对象的指针的行为不同
- 堆变量与否 |* 新对象() ||对象()
- 对象 ** 到对象 * 之间的融合
- 如何创建功能以用受保护或私人侵犯的对象在对象内部操纵对象
- 包含另一个对象的对象,然后将其传递给它(C )
- 围绕另一个对象旋转对象
- 自动将对象添加到该对象父对象的向量中