在c++中查看类中基于参数的私有变量的访问方法

Accessor Method to view private variable based on argument in a class in c++?

本文关键字:变量 方法 访问 参数 c++ 于参数      更新时间:2023-10-16

我的问题是我的类中有许多变量,我希望通过访问器方法访问它们。当然,我可以有几个访问器函数来输出我的私有变量,但我如何才能使它能够通过参数访问它们中的任何一个。我的班级:

class Character {
public:
    void setAttr(string Sname,int Shealth,int SattackLevel,int SdefenseLevel)  {
        name = Sname;
        health = Shealth;
        attackLevel = SattackLevel;
        defenseLevel = SdefenseLevel;
    }
    int outputInt(string whatToOutput)  {
        return whatToOutput //I want this to either be health, attackLevel or defenseLevel
    }
private:
    string name;
    int health;
    int attackLevel;
    int defenseLevel;
};

基本上我想知道的是如何返回一个关于outputInt函数的私有变量。大多数OOP教程都有一个函数来返回每个变量,这在大型程序中似乎是非常不健康的。

c++不支持你想要完成的事情:反射或关于对象的详细运行时信息。在c++中有一种叫做"运行时类型信息"的东西,但是它不能提供关于变量名的信息:原因是,在编译和链接的二进制文件中,这些信息(变量名)将不再必要出现。

但是,您可以使用std::unordered_map而不是普通的整数变量来完成类似的操作。因此可以通过名称访问值,作为字符串。请考虑以下代码:
#include <string>
#include <iostream>
#include <unordered_map>
using namespace std;
class Character {
public:
  void setAttr(const string& Sname, int Shealth, int SattackLevel, int SdefenseLevel) {
    name = Sname;
    values.insert(std::make_pair("health", Shealth));
    values.insert(std::make_pair("attackLevel", SattackLevel));
    values.insert(std::make_pair("defenseLevel", SdefenseLevel));
  }
  int outputInt(const string& whatToOutput) {
    return values.at(whatToOutput);
  }
private:
  string name;
  std::unordered_map<std::string, int> values;
};
int main(int argc, char* argv[]) {
  Character yourCharacter;
  yourCharacter.setAttr("yourName", 10, 100, 1000);
  std::cout << "Health: " << yourCharacter.outputInt("health") <<std::endl;
  std::cout << "Attack level: " << yourCharacter.outputInt("attackLevel") << std::endl;
  std::cout << "Defense level: " << yourCharacter.outputInt("defenseLevel") << std::endl;
  return 0;
}

它将按预期输出:

Health: 10
Attack level: 100
Defense level: 1000

另一个不依赖于unordered_map的选项是,使用预定义的静态字符串作为变量名,使用数组或向量作为值。因此,我们可以将上面的class Character替换为:

static std::string variableNames[3] = {
  "health",
  "attackLevel",
  "defenseLevel"
};
class Character {
public:
  void setAttr(const string& Sname, int Shealth, int SattackLevel, int SdefenseLevel) {
    name = Sname;
    variableValues[0] = Shealth;
    variableValues[1] = SattackLevel;
    variableValues[2] = SdefenseLevel;
  }
  int outputInt(const string& whatToOutput) {
    int retVal = 0;
    for (size_t i = 0; i < sizeof(variableNames)/sizeof(std::string); ++i) {
      if (!whatToOutput.compare(variableNames[i])) {
        retVal = variableValues[i];
      }
    }
    return retVal;
  }
private:
  string name;
  int variableValues[3];
};

仍然得到相同的输出。然而,这里你必须手动管理字符串数组中所有变量名的列表——我不喜欢这种解决方案,我个人更喜欢上面的其他解决方案之一。

在c++中处理这种设计的最常见的方法是使用单独的getHealth(), getAttackLevel(), getDefenseLevel()函数代替。然而,这将错过一个用例,那就是:如果你想让用户输入一个字符串,比如"health",然后显示相应的变量,你需要自己编写代码来调用相应的getXXX()函数。如果这在您的情况下不是问题,请考虑下面的代码,这将更加清晰:
#include <string>
#include <iostream>
using namespace std;
class Character {
public:
  void setAttr(const string& Sname, int Shealth, int SattackLevel, int SdefenseLevel) {
    name = Sname;
    health = Shealth;
    attackLevel = SattackLevel;
    defenseLevel = SdefenseLevel;
  }
  int getHealth() const { return health; }
  int getAttackLevel() const { return attackLevel; }
  int getDefenseLevel() const { return defenseLevel; }
private:
  string name;
  int health, attackLevel, defenseLevel;
};
int main(int argc, char* argv[]) {
  Character yourCharacter;
  yourCharacter.setAttr("yourName", 10, 100, 1000);
  std::cout << "Health: " << yourCharacter.getHealth() <<std::endl;
  std::cout << "Attack level: " << yourCharacter.getAttackLevel() << std::endl;
  std::cout << "Defense level: " << yourCharacter.getDefenseLevel() << std::endl;
  return 0;
}

另一个不相关的建议:不要使用string作为函数的参数类型,而是使用const string& (const reference to string;参见上面的示例代码)。这样可以更容易地调用你的函数(它们可以直接使用字符串字面量调用,而不需要在调用代码中创建额外的变量),而且它们不会产生额外的不必要的复制。唯一的复制将发生在:name = Sname;(在您的代码中发生了两次复制)。

我不知道这对你来说是否是个好主意,但你可以使用一个公共类型的pedef结构,你通过引用传递并设置你的值。

class Character {
public:
    //...
    typedef struct allvalues{
        string vname;
        int vhealth;
        int vattackLevel;
        int vdefenseLevel;
    }allvalues;
    void getValues(allvalues& val){
        val.vname = name;
        val.vhealth = health;
        val.vattackLevel = attackLevel;
        val.vdefenseLevel = detenseLevel;
    }
    //...
};
//...
//somewhere in the code
Character myCarac;

//...
//Here how to use it
Character::allvalues values;
myCarac.getValues(values);