Najszybciej chyba CRTP. Dwa rodzaje interfejsów. Przez klasę zliczającą i przez obiekt. Oczywiście jeszcze można wiele poprawić ale to dość wygodne w kodzie pokazywać explicite obiekty jakich klas są interesujące oraz dostawać się z dowolnej instancji do innych instancji :-)
#include <iostream>
#include <set>
#include <string>
template<class T>
class ClassCounter {
public:
static size_t countInstances() {
return instances.size();
}
static const std::set<const T*>& getInstances(){
return instances;
}
size_t countObjects() const {
return ClassCounter<T>::instances.size();
}
const std::set<const T*>& getObjects() const {
return ClassCounter<T>::instances;
}
protected:
ClassCounter() = default;
static bool addInstance(const T* instance) {
return instances.insert(instance).second;
}
static void removeInstance(const T* instance) {
auto it = instances.find(instance);
if(it != instances.end()) {
instances.erase(it);
}
}
private:
static std::set<const T*> instances;
};
template<class T>
std::set<const T*> ClassCounter<T>::instances;
class Animal: public ClassCounter<Animal> {
public:
explicit Animal(const std::string& name): name{name} {
addInstance(this);
}
~Animal() {
removeInstance(this);
}
friend std::ostream& operator<<(std::ostream& os, const Animal& animal) {
os << "Animal: " << animal.name;
return os;
}
private:
std::string name;
};
class Item: public ClassCounter<Item> {
public:
explicit Item(const std::string& name): name{name} {
addInstance(this);
}
~Item() {
removeInstance(this);
}
friend std::ostream& operator<<(std::ostream& os, const Item& item) {
os << "Item: " << item.name;
return os;
}
private:
std::string name;
};
int main() {
// Access via static class...
auto animal1 = Animal("Dog");
auto animal2 = Animal("Cat");
auto animal3 = Animal("Human");
std::cout << ClassCounter<Animal>::countInstances() << " animal(s) in this zoo.\n";
for(const auto& object: ClassCounter<Animal>::getInstances()) {
std::cout << *object << '\n';
}
// Access via object...
auto item1 = Item("Pen");
auto item2 = Item("Car");
auto item3 = Item("Chair");
std::cout << item1.countObjects() << " item(s) in this box\n";
for(const auto& object: item1.getObjects()) {
std::cout << *object << '\n';
}
}
PS. Delikatnie poprawiłem. Nie trzeba teraz pamiętać o jawnym wołaniu dodawania instancji. Robi to konstruktor klasy zliczającej. Nie trzeba także pamiętać o usunięciu obiektu. Robi to destruktor klasy zliczającej.
#include <iostream>
#include <set>
#include <string>
template<class T>
class ClassCounter {
public:
using instances_container_t = std::set<const T*>;
using objects_container_t = instances_container_t;
static size_t countInstances() {
return instances.size();
}
static const instances_container_t& getInstances(){
return instances;
}
size_t countObjects() const {
return ClassCounter<T>::instances.size();
}
const objects_container_t& getObjects() const {
return ClassCounter<T>::instances;
}
~ClassCounter() {
removeInstance(static_cast<const T*>(this));
}
protected:
ClassCounter() {
addInstance(static_cast<const T *>(this));
}
private:
static bool addInstance(const T* instance) {
return instances.insert(instance).second;
}
static void removeInstance(const T* instance) {
auto it = instances.find(instance);
if(it != instances.end()) {
instances.erase(it);
}
}
static instances_container_t instances;
};
template<class T>
typename ClassCounter<T>::instances_container_t ClassCounter<T>::instances;
class Animal: public ClassCounter<Animal> {
public:
explicit Animal(const std::string& name): name{name} { }
~Animal() { }
friend std::ostream& operator<<(std::ostream& os, const Animal& animal) {
return (os << "Animal: " << animal.name);
}
private:
std::string name;
};
class Item: public ClassCounter<Item> {
public:
explicit Item(const std::string& name): name{name} { }
~Item() { }
friend std::ostream& operator<<(std::ostream& os, const Item& item) {
return (os << "Item: " << item.name);
}
private:
std::string name;
};
int main() {
// Access via static class...
auto animal1 = Animal("Dog");
auto animal2 = Animal("Cat");
auto animal3 = Animal("Human");
std::cout << ClassCounter<Animal>::countInstances() << " animal(s) in this zoo.\n";
for(const auto& object: ClassCounter<Animal>::getInstances()) {
std::cout << *object << '\n';
}
// Access via object...
auto item1 = Item("Pen");
auto item2 = Item("Car");
auto item3 = Item("Chair");
std::cout << item1.countObjects() << " item(s) in this box\n";
for(const auto& object: item1.getObjects()) {
std::cout << *object << '\n';
}
}