Singleton with inheritance, Derived class can't get instance in parent?
Below code instantiates a derived singleton based on an environment variable. Compiler errors saying error C2512: 'Dotted' : no appropriate default constructor
. I don't understand what the compiler is complaining about.
EDIT: Fixed issues with injecting an instance get method that requires defining both a parent and a derivative. By splitting the class definitions in separate header files and including them in Singleton.cpp where the instance function is implemented.
Mainfile – 1
#include <iostream>
#include <string>
#include "Singleton.h"
using namespace std;
int main(){
Singleton::instant().print();
cin.get();
}
Singleton.h
#pragma once
#include <iostream>
using std::cout;
class Singleton{
public:
static Singleton & instant();
virtual void print(){cout<<"Singleton";}
protected:
Singleton(){};
private:
static Singleton * instance_;
Singleton(const Singleton & );
void operator=(const Singleton & );
};
Singleton.cpp
#include "Singleton.h"
#include "Dotted.h"
Singleton * Singleton::instance_ = 0;
Singleton & Singleton::instant(){
if (!instance_)
{
char * style = getenv("STYLE");
if (style){
if (strcmp(style,"dotted")==0)
{
instance_ = new Dotted();
return *instance_;
} else{
instance_ = new Singleton();
return *instance_;
}
}
else{
instance_ = new Singleton();
return *instance_;
}
}
return *instance_;
}
Dotted.h
#pragma once
class Dotted;
class Dotted:public Singleton{
public:
friend class Singleton;
void print(){cout<<"Dotted";}
private:
Dotted(){};
};
a source to share
There are several problems in your code:
-
You want to return the typeSingleton&
orconst Singleton&
. You are currently returning the value that the copy constructor is trying to call, and no such constructor exists. -
Your default constructor in Dotted is probably not available in Singleton. I suggest you make Singleton a friend of Dotted so that it can access this constructor. Although not 100% sure about it. -
You forgot to make the print () function virtual, so your override will not show up. - You put the "friend" in the wrong place; you need to declare Singleton as a friend of Spotted in Dotted Line, not in Singleton.
- You don't have to provide your definition of Singleton :: instant inline as it needs to build a Dotted instance and for that it needs to see the definition of Dotted. So you have to move this to your source file where it can see both the complete Dotted and Singleton definitions respectively.
- You need to put
Singleton* Singleton::instance_ = 0;
in your source file somewhere. - You are missing the else clause in the section
if(!style)
; currently, if the STYLE environment variable is set but not set to "dashed", you end up returning a null singleton.
In addition to the above, I highly recommend avoiding environment variables and singletons . Both are examples of "shared mutable state" and can lead to a lot of confusion. Singletons, although they appeared long ago in "design patterns" books, are now understood as anti-patterns design . This is a much more flexible approach to making the interface you go through just execute once, rather than baking it into existence once in its API.
For example, for your specific case, I would suggest the following:
class Printer
{
public:
virtual ~Printer(){}
virtual void print()const = 0
};
class StringPrinter : public Printer
{
public:
StringPrinter() : _str("") {}
StringPrinter(const std::string& str) : _str(str) {}
StringPrinter(const StringPrinter& o) : _str(o._str) {}
virtual ~StringPrinter(){}
virtual void print()const{ std::cout << _str << std::endl; }
StringPrinter& operator=(const StringPrinter& o){ _str = o._str; return *this;}
private:
std::string _str;
};
Then, in any class where you've used Singleton before, just take the const Printer & object. And print this object. Elsewhere, you can conditionally build a StringPrinter ("Singleton") or StringPrinter ("dotted"). Or maybe some other instance of this interface, although I would suggest using QSettings or some config file instead of environment variables, or at least use MYAPPLICATIONNAME_STYLE instead of STYLE; in other words, if you are going to use an environment variable route, at least qualify its name.
a source to share
This is not a great error message. The problem is that the compiler cannot generate the code to call the constructor, it hasn't seen the definition of the Dotted class yet. C ++ compilers are still one-pass compilers. You cannot write an inline method, you need to move it.
class Singleton {
public:
static Singleton & instant();
// etc..
};
class Dotted : public Singleton {
// etc..
};
// Now it works:
Singleton & Singleton::instant() {
// etc..
}
a source to share
The first error that I get: strcmp not declared
. Hint: in the title <cstring>
(≈ <string.h>
).
After that the following error:
instance_ = new Dotted();
"Invalid use of incomplete type."
If you are using forward declarations, you must separate declarations and implementations so that you can do things that require full types once they are defined.
a source to share