Reputation: 20068
I made a base class object to track instances of some objects. I want to log this in a file like so:
Object Name: Number of instances
Here is my base class:
template <class T>
class CountedObj
{
public:
CountedObj()
{
// write name of derived class and counter to log
}
CountedObj(const CountedObj& obj)
{
// write name of derived class and counter to log
}
~CountedObj()
{
// write name of derived class and counter to log
}
private:
int counter;
mylogger log;
};
My problem is that I want to print the name of the class that will inherit from my CountedObj
, but I can't use typeid
inside the constructor.
Is there any alternative to log which object is allocated and deallocated ?
Upvotes: 1
Views: 980
Reputation: 156
EDIT: here is a better and cleaner version
A solution would be to use an unordered_map in your CountedObj. This is an association map between the name of the class and the number of instances.
Then for each class, store the CountedObj and increase the number of instance in the unordered_map.
#include <stdio.h>
#include <iostream>
#include <string>
#include <unordered_map>
class CountedObj
{
public:
void increase_instance(const std::string& s)
{
if (instances.find(s) == instances.end())
instances[s] = 1;
else
instances[s] += 1;
}
void pretty_print()
{
for(auto& n = instances.begin(); n != instances.end(); ++n)
std::cout << "Name of the class " << n->first << " : instances =>" << n->second << std::endl;
}
private:
std::unordered_map<std::string, unsigned int> instances;
};
class A
{
public:
A(CountedObj& o)
: name("A"),
obj(o)
{
o.increase_instance(name);
}
const std::string& name;
private:
CountedObj& obj;
};
class B
{
public:
B(CountedObj& o)
: name("B"),
obj(o)
{
o.increase_instance(name);
}
const std::string& name;
private:
CountedObj& obj;
};
int main()
{
CountedObj c;
A a(c), aa(c), aaa(c);
B b(c), bb(c);
c.pretty_print();
return 0;
}
Output of the program
Name of the class A : instances =>3
Name of the class B : instances =>2
If you don't want to store the CounterObj in each class, you can define the class CounterObj as a Singleton.
========
I made a base class object to track instances of some objects.
Do you assume that the objects that you are trying to track are related? (for instance a base class A has several child like B and C). If you want to track the different instanciation it is better to have a unique type to store the objects (like in a vector of A).
To follow the number of instanciation made for each class you need to store a static int (class variable) for each of your trackable class.
In each constructor you increase this variable by 1.
#include <iostream>
#include <vector>
#include <string>
class A
{
public:
A(const std::string& n) : name("A")
{
instance_num += 1;
name = n;
}
std::string toString()
{
return name;
}
virtual int instance_num_get()
{
return instance_num;
}
static int instance_num;
protected:
std::string name;
};
class B : public A
{
public:
B()
: A("B")
{
instance_num += 1;
}
int instance_num_get()
{
return instance_num;
}
static int instance_num;
};
class C : public A
{
public:
C()
: A("C")
{
instance_num += 1;
}
int instance_num_get()
{
return instance_num;
}
static int instance_num;
};
For your CountedObj, I used a vector of A* containing one instanciation of each class (we only need one to access the different class variables).
template<typename T>
class CountedObj
{
public:
CountedObj(std::vector<T*>& v)
: obj_list(v)
{
}
void pretty_print()
{
for (auto elt = obj_list.begin(); elt != obj_list.end(); ++elt)
std::cout << (*elt)->toString() << ": " << (*elt)->instance_num_get() << std::endl;
}
private:
std::vector<T*> obj_list;
};
Using pretty_print method, we display the name of the class and the number of instantiation.
We init the class variables. Here is a working example:
int A::instance_num = 0;
int B::instance_num = 0;
int C::instance_num = 0;
int main(void)
{
A a("A");
B b;
C c;
std::vector<A*> vec;
vec.push_back(&a);
vec.push_back(&b);
vec.push_back(&c);
CountedObj<A> count(vec);
count.pretty_print();
return 0;
}
which will give
Output:
A: 3
B: 1
C: 1
I think it would be interesting to use the Observer design pattern which is the idea intended in the solution I provided
Upvotes: 0
Reputation: 29952
You didn't specified how you would use this CountedObj
. I assume this:
class Foo: public CountedObj<Foo> {
};
If you derive further from Foo
, then your counter is already unable to differentiate between Foo, and its derived classes, so I suppose that you want to print "Foo" as the name for all derived classes. If you do want to differentiate between these classes, then a different solution is needed (I'll delete this answer if that's the case).
(Or maybe you can derive again from CountedObj
: class FooDerived: public Foo, public CountedObj<FooDerived> { };
, but this way, FooDerived
will be counted both as FooDerived
and Foo
)
So, you can use typeid this way:
template <typename T>
class CountedObj {
public:
CountedObj() {
counter++;
printf("%s: %d\n", typeid(T).name(), counter);
}
private:
static int counter; // static is needed
};
If you don't like the output from typeid().name()
, then you can add a static name-query function into Foo:
template <typename T>
class CountedObj {
public:
CountedObj() {
counter++;
printf("%s: %d\n", T::name(), counter);
}
...
};
class Foo: public CountedObj<Foo> {
public:
static const char *name() {
return "Foo";
}
};
Upvotes: 1