在控制台应用程序上工作,我试图想出一种允许用户输入对象属性和值的方法。例如
class Box{
public:
int height;
int width;
int length;
};
int main(){
string memberName,value
cin>>memberName>>value;
}
如果用户输入高度,那么通过使用memberName和value,我如何将高度值更改为用户输入的任何值。我想做这个工作,以便可以添加另一个类并获得相同的功能。
我正在考虑使用地图,但不完全确定我将如何继续这样做。
答案 0 :(得分:4)
这是一种复杂的方法,但它确实完成了你想要做的事情(很容易将其应用于任何结构):
#include <iostream>
#include <string>
#include <map>
/////////////////////////////////////////
// HELPERS
template<class T>
class MemberPtrBase
{
public:
virtual std::istream& Read(std::istream& is, T& object) = 0;
virtual ~MemberPtrBase() {}
};
template<class T, class V>
class MemberPtr : public MemberPtrBase<T>
{
V T::*member;
public:
MemberPtr(V T::*ptr)
: member(ptr)
{}
std::istream& Read(std::istream& is, T& object)
{
return is >> (object.*member);
}
};
template<class T>
class MemberMap
{
typedef std::map<std::string, MemberPtrBase<T>*> MapType;
MapType members;
public:
MemberMap() {}
~MemberMap()
{
for (MapType::iterator it = members.begin(); it != members.end(); ++it)
delete it->second;
}
template<class V>
void Register(std::string const& name, V T::*ptr)
{
members[name] = new MemberPtr<T, V>(ptr);
}
std::istream& ReadValue(std::istream& is, T& object)
{
std::string name;
is >> name;
if (members.find(name) == members.end())
{
std::cerr << "Unknown member: " << name << std::endl;
return is;
}
return members[name]->Read(is, object);
}
};
///////////////////////////////////////////
class Box
{
public:
int width;
int height;
int length;
static MemberMap<Box> members;
};
MemberMap<Box> Box::members;
class Circle
{
public:
int x;
int y;
int radius;
static MemberMap<Circle> members;
};
MemberMap<Circle> Circle::members;
int main()
{
Box::members.Register("width", &Box::width);
Box::members.Register("height", &Box::height);
Box::members.Register("length", &Box::length);
Circle::members.Register("x", &Circle::x);
Circle::members.Register("y", &Circle::y);
Circle::members.Register("radius", &Circle::radius);
Box box;
Box::members.ReadValue(std::cin, box);
return 0;
}
答案 1 :(得分:0)
使用地图确实是这样做的方法。将很快编辑代码示例
#include "stdafx.h"
#include <iostream>
#include <map>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
std::map<std::string,std::string> mapData;
std::string name,value;
std::cout << "Enter Name:\n";
std::cin >> name;
std::cout << "\n\nEnter Data\n";
std::cin >> value;
mapData[name] = value;
return 0;
}
或者你可以有一个包装地图的类,如果你需要在地图上或之间进行各种其他操作(例如验证输入或在输入之前对输入进行其他操作)。
// example of class wrapping a map
class MapContainer {
private:
map<string,string> _map;
public:
void addValue(const string& name, const string& value) {
_map[name] = value;
}
string getValue(const string& name) {
return _map[name];
}
}
答案 2 :(得分:0)
简单地说,你不能以一般的方式做到这一点;没有办法根据运行时的名称访问成员变量。
这通常通过创建if-else链(或开关)来解决,该链允许您将数据转换为信息:
char type;
float height;
int age;
cin >> type;
switch(type) {
case 'h': cin >> height; break;
case 'a': cin >> age; break;
}
这是不灵活的,但你的班级结构也是如此,对吧?如果你想让它变得动态,那么在@ riv的答案中创建绑定毫无意义;课堂上的任何变化都需要适当的条款。最好简单地确定一些字段是动态存储在地图上的:
class C {
int iAlwaysHaveThat;
map<variant<int,float,string>> dynamicValues;
};
这样您就可以解析配置文件,例如:
Obj [
a = 5,
b = 3.14,
name = "some string value"
]
然后通过(根据实际需要)访问它们返回optional
:
optional<variant<...>> getDynamicValue(string const& name) { ... }
还可以根据其他对象填写默认值,例如:
{
// ...
auto v = dynamicValues.find(name);
if (v == dynamicValues.end())
return defaultValuesProvider.getDefault(name);
// ...
}
这是一个对某些用途实际上有用的解决方案,但无论如何我都可能会嵌入一个脚本语言。