Making a polymorphic logger

Nov 1, 2008 at 4:35am
I'm doing some more C++ learning here, and I have some questions about cleaning up a vector that contains abstract base classes.

I created a simple "console" that you can feed a string, and have that string logged in a number of different ways. For example, you could tell the console to print "Hello world" and that string would be written to a string buffer, a file, and maybe even a database. The point is that the string can be logged in a variety of ways. I wanted the usage to feel something like this:

1
2
3
4
5
console.addLogger(stringLogger)
console.addLogger(fileLogger)
console.addLogger(databaseLogger)

consol.print("Hello world!")


So far I have created an abstract base class based on the tutorial on this website (http://www.cplusplus.com/doc/tutorial/polymorphism.html). This class is called Logger. Logger is extended by another class called StringLogger, which uses a stringstream internally.

The Console class contains a private vector<Logger*>. It also has a function addLogger(Logger* logger). My question is this:

When the destructor of the Console is called, should I have it delete all the Logger* instances in the vector, or should I expect the user of these classes to do that himself?

This question is causing me grief because I can imagine the class being used two ways:

Way 1:
1
2
3
4
5
6
7
8
StringLogger logger;

Console console;
console.addLogger(&logger);

// Send text to the StringLogger
console.print("This is a test");
console.print("This is also a test.");

If used this way, deleting the Logger* instances held by the vector causes a crash since new was never used. However, if used *only* in this manner, with new never being used, the Console would not need to delete anything.

Way 2:
1
2
3
4
5
6
Console console;
console.addLogger(new StringLogger());

// Send text to the StringLogger
console.print("This is a test");
console.print("This is also a test.");

In this case, deleting from the destructor of the Console makes it so the user does not have to manually delete anything. However, if someone uses the "address off" technique in Way 1, the program will bomb when it starts deleting things.

So, from a best practice sort of perspective, which is a better design? Deleting Logger*'s for the user and expecting him to use new (and avoiding address of), OR deleting nothing, and expecting the user to use address of and clean up after himself if he uses new?
Nov 1, 2008 at 3:58pm
There is no best practice. The question you have is one of ownership -- who owns the objects that are being inserted into the vector. This is a $64,000 question when it comes to programming.

You make very valid points in either approach, and there is no "right" answer.

There are other alternatives, none of which are "right" either. For example, you could store boost::shared_ptr<>s in your vector, and the the object is destroyed when the last reference is deleted. However, even it isn't the silver bullet because someone might construct a shared_ptr from a raw pointer, then delete the raw pointer underneath.

Ultimately you have to decide who owns the objects and then code to that specification.

Nov 7, 2008 at 11:13pm
Perhaps you could add a pure virtual clone() method to the Logger class and call it inside addLogger(). Then the console object could maintain it's own copy of the functionality you desire and the user can maintain his own end of things.

Forgive me if I don't explain this 100% accurately. Maybe jsmith has some insight regarding this idea; I'm not sure if it's any better than any of the other ideas mentioned so far. I think this approach is called the prototype design pattern.

Last edited on Nov 10, 2008 at 4:02pm
Topic archived. No new replies allowed.