Reputation: 429
I defined a C++ class and numerous objects of that class get created during my program's runtime.
I need a get_object_by_name
method.
This is what I would do in 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
How can I do that in c++?
Upvotes: 4
Views: 1224
Reputation: 5698
C++ has no such feature as all_instances
in python.
You have to manage this yourself.
First you need to store your different objects of the class in a container (e.g. std::list<Person> persons
.
The get_object_by_name
would look like this
Person & get_object_by_name(const std::string &name) {
for (auto & person : persons) {
if (person.get_name() == name) {
return person;
}
}
}
Person
needs to have a method get_name()
. Alternatively, you can overload the operator ==
. get_object_by_name
needs access to persons
. Thus it is a good idea to put them into a class
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;
};
As SPD pointed out the choice of the container is not trivial. If you would use and std::vector
and it grows over time it will cause a re-allocation and thus all references returned are invalidated.
Upvotes: 1
Reputation: 8217
As mentioned in the other answers, this kind of feature does not exist in C++ and you have to code it yourself. You already have some examples of how to achieve it by defining external storage class and keep track of the objects by yourself. Below is another approach that delegates the responsibility of objects tracking to the Person
class itself. Additional assumptions:
explicit
keyword to make it converting constructor)delete
on objects allocated on the stackclass 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
}
Expected output:
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);
Example verified with valgrind.
Upvotes: 0
Reputation: 701
You can use either unordered_map (hash map, requires C++11) or map (rbtree) to simulate Python's dictionaries.
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;
}
Keep in mind that there is no automatic memory management in C++, so you need to store your objects somewhere to prevent memory leaks or dangling pointers. If you want objects
to own the objects, then replace raw pointer with unique_ptr (or shared_ptr depending on use-cases):
std::unordered_map<string, std::unique_ptr<Object>> objects;
Upvotes: 1