I checked several answers on the forums about this topic, but for some reason I still cannot wrap my mind about the problem and get my works running.
My aim is to have one object of a class available in all translation units. Note, I do not seek to implement a singleton pattern here. There can be multiple instances of the class, but there will always be at least one and this one can be seen where "its header" is included.
Assume a design like this:
---logging.h---
class LogCenter {
...
}
class Logger {
private:
LogCenter& center;
public:
Logger(); //automatically assigns the ominous global LogCenter object (reference) to center.
Logger(LogCenter&); //however, any other LogCenter object would work equally fine.
}
The reason? I want that all Logger
s can use this global LogCenter
per default, wherever they may be used.
But now I'm unsure how to provide this global LogCenter
. It has the default constructor. Do I just add a line extern LogCenter globalCenter;
into the logging.h
file and another LogCenter globalCenter;
line into the logging.cpp
file? Or just LogCenter globalCenter;
in the logging.cpp
and extern LogCenter globalCenter;
in any file that uses it?
I am really confused, none of which I tried worked - however, the overall code works fine when I use the constructor overload with its own LogCenter
instance.
EDIT: Important: extern
goes with declaration (e.g. in header file). Then one single definition in an implementation file.
Check all the answers!
This is how it works:
---logging.h---
class Entry;
class LogCenter {
friend class Logger; //please don't bash my friend design right away... D:
private:
list<Entry> entries;
public:
void printLog(string file);
}
extern LogCenter SCenter;
class Logger {
private:
LogCenter& center;
public:
Logger(); //uses extern or "singleton" object.
Logger(LogCenter&);
void commitEntry(Entry); //adds the Entry object to the list in the center object.
}
-----------------
---logging.cpp---
#include "logging.h"
LogCenter SCenter;
void Logger::commitEntry(Entry e) {
entries.push_back(e); //Logger can access LogCenter fields!
}
void LogCenter::printLog(string file) {
//open file stream str
for(list<Entry>::iterator it = entries.begin();...)
str << it->getEntry() << endl;
//close file
}
--------------
---main.cpp---
#include "logging.h"
int main() {
Logger log;
Entry e("Some entry");
log.commitEntry(e);
SCenter.printLog("filename.log");
//or
LogCenter cent;
Logger log(cent);
Entry e("some entry");
log.commitEntry(e);
cent.printLog("filename.log");
return 0;
}
-------------
I recommend you to use the following C++ singleton pattern which is easy to use and even safe when using it across shared libraries:
// LogCenter.h
class /*API_MACRO_FOR_EXPORTING*/ LogCenter {
public:
static LogCenter* instance();
}
// For quick access you could define a macro
#define sLogCenter \
LogCenter::instance()
// LogCenter.cpp
LogCenter* LogCenter::instance()
{
static LogCenter instance;
return &instance;
}
// LogCenter::instance()->DoSomething();
// sLogCenter->DoSomething();
To answer your question about extern:
// LogCenter.h
// You can use multiple definitions with extern so the compiler
// knows the LogCenter is instantiated somewhere else.
extern LogCenter globalCenter;
// LogCenter.cpp
// Only 1 declaration of LogCenter
/*API_MACRO_FOR_EXPORTING*/ LogCenter globalCenter;