Hi,

I am playing around and trying to understand template singleton class and I accidentally created something that I am surprise it even runs.

What I don't understand is when did Apple and Orange instances get created?
Could you also comment on the good and bad of the Singleton class if it is proper?

// singleton template test

#include <iostream>

template <typename T> class Singleton
{
private:
	Singleton(const Singleton<T>&){};
	Singleton& operator=(const Singleton<T>&){};

protected:
	static T* _mInstance;
	static int i; // for debugging purposes
	Singleton()
	{
		i++; // for debugging purposes
		_mInstance = this;

		// this function never called?
		std::cout << "created" << std::endl;
	};

public:
	static T& getSingleton()
	{
		return *_mInstance;
	}
	static T* getSingletonPtr()
	{
		return _mInstance;
	}
};
template <typename T> T* Singleton<T>::_mInstance;
template <typename T> int Singleton<T>::i = 0; // for debugging purposes

class Orange : public Singleton<Orange>
{
public:
	void squeeze()
	{
		std::cout << "squeeze" << i << std::endl;
	}
};

class Apple : public Singleton<Apple>
{
public:
	void cut()
	{
		std::cout << "cut" << i << std::endl;	
	}
};

int main()
{
	Orange::getSingletonPtr()->squeeze();
	Apple::getSingleton().cut();
	
	return 0;	
}

Recommended Answers

All 3 Replies

An even more crazy version? What happened?

#include <iostream>

template <typename T> class Singleton
{
private:
	Singleton(const Singleton<T>&){};
	Singleton& operator=(const Singleton<T>&){};

protected:
	static T* _mInstance;
	Singleton()
	{
		This is crazy! This is not a comment
			but it just gets away from the compiler!

			OMG, what is this?!
	};

public:
	static T& getSingleton()
	{
		return *_mInstance;
	}
	static T* getSingletonPtr()
	{
		return _mInstance;
	}
};
template <typename T> T* Singleton<T>::_mInstance;

class Apple : public Singleton<Apple>
{
public:
	void cut()
	{
		std::cout << "cut" << std::endl;	
	}
};

int main()
{
	Apple::getSingletonPtr()->cut();

	return 0;	
}

>What I don't understand is when did Apple and Orange instances get created?
They didn't. You're invoking undefined behavior because _mInstance is a null pointer. However, neither squeeze nor cut access non-static data members, so there's a good chance that both functions will run properly.

Here's an example of the phenomenon:

#include <iostream>

class Example {
public:
  void print() { std::cout<<"It works?!\n"; }
};

int main()
{
  // Undefined! Don't rely on this
  ((Example*)0)->print();
}

It might blow up on you, that's how undefined behavior works, but the idea is that member functions are actually just regular functions with a hidden this parameter. The above example might be translated by the compiler into something like so:

#include <iostream>

class Example {};

void print__Example_00 ( Example * const this )
{
  std::cout<<"It works?!\n";
}

int main()
{
  // Undefined! Don't rely on this
  print__Example_00 ( (Example*)0 );
}

Notice that the this parameter isn't ever dereferenced, that's the key. If you try to access an instance member of the class, then the pointer would be dereferenced and you'd suffer whatever ills occur when trying to access a null pointer.

>An even more crazy version? What happened?
Templates that aren't called don't get instantiated. You can have all kinds of crap that causes a compiler to puke and it'll go through cleanly if you never instantiate the template.

Yea, I sort got got that kind of understanding after a few more hours of trial and error. [ but i could not find the delete button for the post ] Your reply further reinforced my guess. Thanks. Thanks.

Anyways, it was an overwhelming debugging experience....

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.