C++:跟踪类对象

4

我定义了一个C++类,并且在程序运行期间创建了许多该类的对象。

我需要一个get_object_by_name方法。

这是在Python中我会做的:

class Person():
    all_instances = []
    def __init__(self, name, age):
        self.name = name
        self.age = age 
        self.all_instances.append(self)
    @classmethod
    def get_obj_by_name(cls, name):
        for obj in cls.all_instances:
            if obj.name == name:
                return obj 

我该如何使用C++完成这个任务?


1
你可以将它们记录在std::map <std::string,MyClass>中。 - Paul Sanders
1
静态的 std::vector 成员变量? - Algirdas Preidžius
4
Python中的类变量在C++中有相当于“静态(static)”成员的概念,但您确定要这样做吗?这听起来像是一个不良设计,应该用其他方式解决。 - Yksisarvinen
这里有一个类似但不完全相同的答案,链接在这里:https://stackoverflow.com/a/60142588/10871073。 - Adrian Mole
1
@Sky 这违反了单一职责原则 - 你的类既执行它设计的功能,存储自身的实例。此外,这种实现背后的用例通常也有坏味道。我不是说它一定不好(缺少信息),但值得再次检查。 - Yksisarvinen
显示剩余4条评论
3个回答

1
你可以使用 unordered_map(哈希映射,需要C++11)或 map(红黑树)来模拟 Python 的字典。
class Object {
public: // for simplicity. You will want ctor, getters and setters instead.
    std::string name;
    // other fields...
};

std::unordered_map<string, Object*> objects;

Object * get_obj_by_name(const std::string &name) {
    auto map_iterator = objects.find(name);
    return map_iterator == objects.end() ? nullptr : map_iterator->second;
}

请注意,在C++中没有自动内存管理,所以你需要将你的对象存储在某个地方以防止内存泄漏或悬空指针。如果你想让对象 拥有 这些对象,那么用unique_ptr (或者根据使用情况选择shared_ptr)来替换原始指针:

std::unordered_map<string, std::unique_ptr<Object>> objects;

1

C++没有像Python中的all_instances这样的功能。

你需要自己管理。

首先,你需要将类的不同对象存储在一个容器中(例如:std::list<Person> persons)。

get_object_by_name应该是这样的:

Person & get_object_by_name(const std::string &name) {
    for (auto & person : persons) {
        if (person.get_name() == name) {
            return person;
        }
    }
}

Person需要有一个方法get_name()。或者,您可以重载运算符==get_object_by_name需要访问persons。因此,将它们放入一个类中是个好主意。

class Persons{
  public:
    Person & get_object_by_name(const std::string &name);
    // constructor to fill persons
    // method to fill persons

  private:
    std::list<Person> persons;
};

正如SPD所指出的,容器的选择并不是微不足道的。如果您使用std::vector并且它随着时间的推移增长,它将导致重新分配,从而使所有返回的引用无效化。

0

正如其他答案中所提到的,C++ 中不存在这种功能,你必须自己编写代码实现。你已经有一些例子来说明如何通过定义外部存储类并自己跟踪对象来实现它。以下是另一种方法,将对象跟踪的责任委托给Person 类本身。额外假设:

  • 你只能使用参数化构造函数(或删除explicit关键字使其成为转换构造函数)
  • 人员的名称必须是唯一的
  • 你会小心并不会尝试在堆栈上分配的对象上调用delete
class Person
{
public:
  /* for example simplifity I delete all the other CTors */
  Person() = delete;
  Person(const Person& other) = delete;
  Person(Person&& other) = delete;
  explicit Person(const std::string& name): name_(name)
  {
    std::cout << "Person(" << name_ << ");" << std::endl;
    if (all_instances_.count(name_) != 0)
    {
      std::cout << "Person with name " << name_ << " already exists" << std::endl;
      throw std::runtime_error("Person with that name already exists");
    }
    all_instances_.emplace(std::make_pair(name_, this));
  }
  ~Person()
  {
    std::cout << "~Person(" << name_ << ");" << std::endl;
    all_instances_.erase(name_);
  }
  static Person* get_person_by_name(const std::string& name)
  {
    if (all_instances_.count(name) == 0)
    {
      std::cout << "Person with name " << name << " does not exist" << std::endl;
      return nullptr;
    }
    return all_instances_.find(name)->second;
  }

private:
  static std::map<std::string, Person*> all_instances_;
  std::string name_;
};

std::map<std::string, Person*> Person::all_instances_;


int main(int argc, char* argv[])
{
  Person p1("person1");
  try
  {
    Person p2("person1");  // exception
  }
  catch (const std::exception& e)
  {
    std::cout << "Exception: " << e.what() << std::endl;
  }
  {
    Person p3("person3");
    Person* p4 = new Person("person4");
    new Person("person5");  // lost pointer, but later we get it from the map
    delete p4;
    // p3 out of scope
  }
  auto person5 = Person::get_person_by_name("person5");
  delete person5;
  auto person1 = Person::get_person_by_name("person1");
  if (person1) std::cout << "Person1 still exists" << std::endl;
  auto person3 = Person::get_person_by_name("person3");
  if (!person3) std::cout << "Person3 does not exist anymore" << std::endl;
  return 0;
  // p1 out of scope
}

预期输出:

Person(person1);
Person(person1);
Person with name person1 already exists
Exception: Person with that name already exists
Person(person3);
Person(person4);
Person(person5);
~Person(person4);
~Person(person3);
~Person(person5);
Person1 still exists
Person with name person3 does not exist
Person3 does not exist anymore
~Person(person1);

使用valgrind验证的示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接