Single-instance classes

Started by
8 comments, last by MaulingMonkey 18 years ago
Say that I need only one instance of a certain class: for example, I cound have a CLogFile class, and I only want one logfile to be in the application plus I want it to be globally accessible. I don't need class inheritance. There are several solutions to this problem.. I could create a global instance of the class, and I could solve the problem of undefined order of creation/destruction of globals by using my own Init() and Term() functions:

//---------------------------------------------------------------
// LogFile.h
//---------------------------------------------------------------
#include <cstdio>

class CLogFile
{
public:
	CLogFile() { }
	~CLogFile() { }

	void Init();
	void Term();

	void Print( char* );

private:
	FILE* m_File;
};

extern CLogFile g_Log;

//---------------------------------------------------------------
// LogFile.cpp
//---------------------------------------------------------------
#include "LogFile.h"

CLogFile g_Log;

void CLogFile::Init()
{
	// init
}

void CLogFile::Term()
{
	// terminate
}

void CLogFile::Print( char* )
{
	// do stuff
}

//---------------------------------------------------------------
// Main.cpp:
//---------------------------------------------------------------
g_Log.Init();
g_Log.Print( "Hello, world!" );
g_Log.Term();

Another possibility is to use some variant of the singleton pattern. With it we can ensure there is only one instance of the class, it is globally accessible and it is created when requested, and we could add a static function to destroy the instance to be called at the end of the program..

//---------------------------------------------------------------
// LogFile.h
//---------------------------------------------------------------
#include <cstdio>

class CLogFile
{
	CLogFile();

public:
	~CLogFile();

	void Print( char* );

	static CLogFile*	GetInstance();
	static void		DestroyInstance();

private:
	
	static CLogFile* m_Instance;

	FILE* m_File;
};

//---------------------------------------------------------------
// LogFile.cpp
//---------------------------------------------------------------
CLogFile* CLogFile::m_Instance = NULL;

CLogFile* CLogFile::GetInstance()
{
	if( !m_Instance )
	{
		m_Instance = new CLogFile();
	}
	return m_Instance;
}

void CLogFile::DestroyInstance()
{
	delete m_Instance;
	m_Instance = NULL;
}

void CLogFile::Print( char* )
{
	// do stuff
}

//---------------------------------------------------------------
// Main.cpp:
//---------------------------------------------------------------
CLogFile::GetInstance()->Print( "Hello, world!" );
CLogFile::DestroyInstance();

My point is that there is a much simpler solution: the LogFile doesn't need to be a class. Its functions could just go in a namespace, including Init() and Term(). The code, in my opinion, is more clean, and encapsulation is better because all the private members of CLogFile are hidden in LogFile.cpp. There are less #include files in the header, resulting in faster compile times and less dependencies.

//---------------------------------------------------------------
// LogFile.h
//---------------------------------------------------------------
namespace LogFile
{
	void Init();
	voit Term();

	void Print( char* );
};

//---------------------------------------------------------------
// LogFile.cpp
//---------------------------------------------------------------
#include "LogFile.h"
#include <stdio.h>

namespace LogFile
{
	FILE* m_File;

	void Init()
	{
		// init
	}
	void Term()
	{
		// terminate
	}

	void Print( char* )
	{
		// do stuff
	}
}

//---------------------------------------------------------------
// Main.cpp:
//---------------------------------------------------------------
LogFile::Init();
LogFile::Print( "hello, world!" );
LogFile::Term();

The problem is that, seeing other people's code, this solution is seldom used, and people tend to use classes just about anywhere. I was pretty shocked when I saw code like this in some open source engine:

class Collision
{
	static void PointSphere( Vec3& v, Sphere& s );
	static void SphereSphere( Sphere& s1, Sphere& s2 );
}
What's the point in using a class with only static methods? Those functions are just collision routines that share no data and should go into a common namespace IMHO.
Advertisement
Globals are commonly regarded as bad practice. While there is some freedom here as why and where, it's generally better to not do it.

Quote:
class Collision
{
static void PointSphere( Vec3& v, Sphere& s );
static void SphereSphere( Sphere& s1, Sphere& s2 );
}


Since globals are bad practice, they converted their globals into OOP. Of course, the way they did it is pointless, but technically, they are now OO.

See this thread for some debate about it:
http://www.gamedev.net/community/forums/topic.asp?topic_id=386482

Quote:
What's the point in using a class with only static methods? Those functions are just collision routines that share no data and should go into a common namespace IMHO.


Usually they would be defined as members of sphere class.
If you dont need multiple instances, why use a class?
-----------------------------Language: C++API: Win32, DirectXCompiler: VC++ 2003
Does anyone else reckon that if namespaces had private sections like classes, it might help discourage needless use of single-instance classes or am I talking rubbish as usual?
Quote:Original post by simon10k
If you dont need multiple instances, why use a class?


That's basically what I meant to say. I won't use a class, but so many persons do it, and I want to understand why.

Quote:Original post by Antheus
Since globals are bad practice, they converted their globals into OOP. Of course, the way they did it is pointless, but technically, they are now OO.


But this does not solve anything. I don't see any reason to do it, maybe people is a little too obsessed with OOP.

Quote:
Usually they would be defined as members of sphere class.


I don't think this type of function should really belong to a class, for example, should the SphereBoxCollide() function go into CSphere or CBox? What's the problem in making them standalone routines, in a Collision.cpp file?

Quote:Original post by EasilyConfused
Does anyone else reckon that if namespaces had private sections like classes, it might help discourage needless use of single-instance classes or am I talking rubbish as usual?


Anyway, no one should be able to access the namespace data you define in your .cpp file but don't declare in the header, so it's kind of private.
Quote:Original post by EasilyConfused
Does anyone else reckon that if namespaces had private sections like classes, it might help discourage needless use of single-instance classes or am I talking rubbish as usual?


You can put all 'private' data in the anonymous namespace in the source file that implements the namespace functions for an equivilent effect.
Quote:Original post by simon10k
If you dont need multiple instances, why use a class?


And a chorus of angels came down from Heaven singing with joy!

Quote:Original post by Arcibald Wearlot
Quote:
Usually they would be defined as members of sphere class.

I don't think this type of function should really belong to a class, for example, should the SphereBoxCollide() function go into CSphere or CBox? What's the problem in making them standalone routines, in a Collision.cpp file?


The STL would seem to agree with you. [grin]

And, by the way, you should probably use iostream instead of cstdio if you have a choice. [wink] I prefer C to C++ in general, but when in Rome...
Quote:Original post by Arcibald Wearlot
Quote:Original post by EasilyConfused
Does anyone else reckon that if namespaces had private sections like classes, it might help discourage needless use of single-instance classes or am I talking rubbish as usual?


Anyway, no one should be able to access the namespace data you define in your .cpp file but don't declare in the header, so it's kind of private.


Exactly.

By the way, that's just how they used to do things like "information hiding" in C. (Sometimes you will also see void* mentioned in headers simply to avoid disclosing knowledge of certain *types*.)
Quote:You can put all 'private' data in the anonymous namespace in the source file that implements the namespace functions for an equivilent effect.


Appreciate that but I was thinking more about hiding implementation from other parts of the same translation unit. Is there any technical reason you couldn't have a private section to a namespace or is it just that it wouldn't be considered useful enough to bother?

Quote:Original post by Arcibald Wearlot
My point is that there is a much simpler solution: the LogFile doesn't need to be a class. Its functions could just go in a namespace, including Init() and Term(). The code, in my opinion, is more clean, and encapsulation is better because all the private members of CLogFile are hidden in LogFile.cpp. There are less #include files in the header, resulting in faster compile times and less dependencies.


On the other hand, look at the disadvantages. If you ever decide, well, you really do need a second LogFile, basically all your code using that "class" will break. Since nobody can perfectly predict their future needs, this means that you'll end up rewriting more code. Maybe not for LogFile, but for some other class where you used the same logic. The problem is that by reducing it to these "bare essentials", everything using that code has to be aware of those "bare essentials" - which in this case, involves "there's only one LogFile".

The problem arises with:
1) Directly using singletons2) Directly using globals3) Directly using any single instance of the class


Let's apply this to the wonderful world of "Hello, World!".

The simplest implementation directly uses the global std::cout:

#include <iostream>void greet( void ) {    std::cout << "Hello, ";    std::cout << " World!";    std::cout << std::endl;}int main () {     greet();}


Okay, so I made this example a little more complicated than it needed to be. But, it better illustrates a repeated use of globals, as we might use them in a more complex scenario. What happens we decide, okay, we want to save this wonderful greeting of ours to a file? greet() has to be entirely rewritten. The same occurs if we're using a singleton.

Now, let's change things a bit. Instead of having greet() directly use the global std::cout, let's have it take a paremeter:

#include <iostream>void greet( std::ostream & os ) {    os << "Hello, ";    os << " World!";    os << std::endl;}int main() {    greet( std::cout );}


Just another way of implementing the same thing, right? The advantage here, though, is that greet is blissfully unaware that it's using std::cout. It dosn't need to know, there's only one stream. When we want to output to a file, we need only rewrite main(), where we only have to replace one instance of std::cout, instead of 3:

int main() {    std::ofstream file( "greeting.txt" );    greet( file );}


Not only that, but our program can now use multiple streams, whereas greet was formerly hardcoded to only using one:

int main() {    std::ofstream file( "greeting.txt" );    greet( file );    greet( std::cout );}


The cost? An extra reference/pointer as a parameter/class-member (without which, greet would hardly be "much simpler"). The gain? Flexibility.

As for the "class Collision" thing might be to deal with older compilers that don't support namespaces well. That's the only real reason I can think of for an all-statics class like that.

This topic is closed to new replies.

Advertisement