在C ++中动态更改类的属性

时间:2013-05-27 12:38:08

标签: c++ object

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

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

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

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

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

3 个答案:

答案 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);
    // ...
}

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