C ++: отслеживание объектов класса - PullRequest
4 голосов
/ 10 февраля 2020

Я определил класс 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 ++?

Ответы [ 3 ]

1 голос
/ 10 февраля 2020

Вы можете использовать unordered_map (ha sh map, требуется C ++ 11) или map (rbtree) для имитации словарей 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 управления памятью в C ++ , поэтому вам нужно хранить свои объекты где-нибудь, чтобы предотвратить утечки памяти или висячие указатели. Если вы хотите от objects до владеть объектами, то замените необработанный указатель на unique_ptr (или shared_ptr в зависимости от вариантов использования):

std::unordered_map<string, std::unique_ptr<Object>> objects;
1 голос
/ 10 февраля 2020

C ++ не имеет такой функции, как all_instances в python.

Вы должны управлять этим самостоятельно.

Сначала вам нужно хранить различные объекты класса в контейнере. (например, 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;
};

Как указала СПД, выбор контейнера не тривиален. Если вы используете и std::vector, и со временем он увеличивается, что приведет к перераспределению, и, таким образом, все возвращенные ссылки станут недействительными.

0 голосов
/ 10 февраля 2020

Как уже упоминалось в других ответах, такого рода функция не существует в C ++, и вы должны кодировать ее самостоятельно. У вас уже есть несколько примеров того, как этого добиться, определив класс внешнего хранилища и самостоятельно отслеживая объекты. Ниже представлен другой подход, который делегирует ответственность за отслеживание объектов самому классу Person. Дополнительные предположения:

  • Вы можете использовать только параметр c конструктор (или удалить ключевое слово 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 .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...