1

在控制台应用程序上工作,我试图找出一种允许用户输入对象属性和值的方法。例如

class Box{
    public:
    int height;
    int width;
    int length;
};

int main(){
    string memberName,value
    cin>>memberName>>value;
}

如果用户输入高度,那么通过使用 memberName 和 value 如何将高度的值更改为用户输入的任何值。我想完成这项工作,以便可以添加另一个类并获得相同的功能。

我正在考虑使用地图,但不完全确定我将如何继续这样做。

4

3 回答 3

5

这是一种复杂的方法,但它完全符合您最初想要做的事情(轻松地将其应用于任何结构):

#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;
}
于 2013-05-27T13:04:25.480 回答
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];
   }


}
于 2013-05-27T12:41:30.960 回答
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);
    // ...
}

这是一个对某些用途实际上可能有用的解决方案,但是无论如何我可能会嵌入脚本语言。

于 2013-05-27T13:33:22.200 回答