2017-09-29 6 views
0

Je découvre le modèle de conception singleton avec le chapitre 29 de Professional C++, deuxième édition . Il illustre une implémentation singleton d'une classe Logger qui couvre les exigences de sécurité des threads:Libération de membres de classe utilisés dans différents contextes de synchronisation

tête

#include <iostream> 
#include <fstream> 
#include <vector> 
#include <string> 
#include <mutex> 
// Definition of a multithread safe singleton logger class 
class Logger 
{ 
    public: 
     static const std::string kLogLevelDebug; 
     static const std::string kLogLevelInfo; 
     static const std::string kLogLevelError; 
     // Returns a reference to the singleton Logger object 
     static Logger& instance(); 
     // Logs a single message at the given log level 
     void log(const std::string& inMessage, 
       const std::string& inLogLevel); 
     // Logs a vector of messages at the given log level 
     void log(const std::vector<std::string>& inMessages, 
       const std::string& inLogLevel); 
    protected: 
     // Static variable for the one-and-only instance 
     static Logger* pInstance; 
     // Constant for the filename 
     static const char* const kLogFileName; 
     // Data member for the output stream 
     std::ofstream mOutputStream; 
     // Embedded class to make sure the single Logger 
     // instance gets deleted on program shutdown. 
     friend class Cleanup; 
     class Cleanup 
     { 
      public: 
      ~Cleanup(); 
     }; 
     // Logs message. The thread should own a lock on sMutex 
     // before calling this function. 
     void logHelper(const std::string& inMessage, 
        const std::string& inLogLevel); 
    private: 
     Logger(); 
     virtual ~Logger(); 
     Logger(const Logger&); 
     Logger& operator=(const Logger&); 
     static std::mutex sMutex; 
}; 

mise en œuvre

#include <stdexcept> 
#include "Logger.h" 
using namespace std; 

const string Logger::kLogLevelDebug = "DEBUG"; 
const string Logger::kLogLevelInfo = "INFO"; 
const string Logger::kLogLevelError = "ERROR"; 
const char* const Logger::kLogFileName = "log.out"; 
Logger* Logger::pInstance = nullptr; 
mutex Logger::sMutex; 

Logger& Logger::instance() 
{ 
    static Cleanup cleanup; 
    lock_guard<mutex> guard(sMutex); 
    if (pInstance == nullptr) 
     pInstance = new Logger(); 
    return *pInstance; 
} 
Logger::Cleanup::~Cleanup() 
{ 
    lock_guard<mutex> guard(Logger::sMutex); 
    delete Logger::pInstance; 
    Logger::pInstance = nullptr; 
} 
Logger::~Logger() 
{ 
    mOutputStream.close(); 
} 
Logger::Logger() 
{ 
    mOutputStream.open(kLogFileName, ios_base::app); 
    if (!mOutputStream.good()) { 
     throw runtime_error("Unable to initialize the Logger!"); 
    } 
} 
void Logger::log(const string& inMessage, const string& inLogLevel) 
{ 
    lock_guard<mutex> guard(sMutex); 
    logHelper(inMessage, inLogLevel); 
} 
void Logger::log(const vector<string>& inMessages, const string& inLogLevel) 
{ 
    lock_guard<mutex> guard(sMutex); 
    for (size_t i = 0; i < inMessages.size(); i++) { 
     logHelper(inMessages[i], inLogLevel); 
    } 
} 
void Logger::logHelper(const std::string& inMessage, 
    const std::string& inLogLevel) 
{ 
    mOutputStream << inLogLevel << ": " << inMessage << endl; 
} 

Il poursuit en expliquant pourquoi la classe ami Cleanup est introduit:

Le Cleanup classe est là pour vous assurer que l'instance Logger unique est supprimé correctement à l'arrêt du programme. Ceci est nécessaire car cette implémentation alloue dynamiquement l'instance Logger par en utilisant le nouvel opérateur dans un bloc de code protégé par un mutex. Une instance statique de la classe Cleanup sera créée la première fois que la méthode instance() s'appellera . Lorsque le programme se termine, l'exécution C++ va détruire cette instance Cleanup statique, ce qui déclenchera la suppression de l'objet Logger et un appel au destructeur Logger pour fermer le fichier.

Je trouve très déroutant qu'il déclare « Cela est nécessaire parce que ... », comme s'il n'y avait pas d'autre alternative.

Mes questions:

1) Est-il vraiment nécessaire? Ne serait-il suffisant d'avoir juste toutes les manipulations dans le destructor ?, comme:

Logger::~Logger() 
{ 
    { 
     lock_guard<mutex> guard(Logger::sMutex); 
     delete Logger::pInstance; 
     Logger::pInstance = nullptr; 
    } 
    mOutputStream.close(); 
} 

2) Si la réponse à 1) est, je voudrais savoir « oui, il est en effet nécessaire! » Pourquoi.

Professional C++, deuxième édition par Marc Grégoire, Nicholas A. Solter, Scott J. Kleper Éditeur: Wrox Date de publication: Octobre 2011

+1

Qu'est ce qui déclencherait le destructeur de 'Logger'? – Mat

+0

Toujours libérer ce que vous allouez, ou vous auriez une fuite de ressources. Rappelez-vous que tous les systèmes d'exploitation ne le font pas pour vous. –

+2

BTW, utilisez Singers Singleton et vous n'aurez pas besoin de cela (l'instance n'est pas un pointeur). – Jarod42

Répondre

1

Oui, cela est nécessaire dans ce cas. Puisque le livre a utilisé new et a distribué un pointeur, aucun objet ne sortira de sa portée et le tireur sera déclenché. La seule façon de le faire est d'appeler delete quelque part sur ce pointeur. Au lieu de vous demander de le faire, la classe Cleanup a été créée pour cela.

Tout cela peut cependant être évité si vous utilisez un Meyers Singleton. Cela utilise une variable statique du type singleton et renvoie un pointeur/référence à cela. Cela sera automatiquement détruit à la fin du programme contrairement à la version livre. Un Meyers Singleton ressemble à:

class Singleton { 
public: 
    static Singleton* Instance() { static Singleton s; return &s; } 
    Singleton(const Singleton&) = delete; 
    void operator=(const Singleton&) = delete; 
private: 
    Singleton() = default; 
}; 
+0

Vous répondez me laisse me demander si le livre référé est bon. Je veux dire, j'ai beaucoup appris en comparant sa variante proposée de singleton à l'alternative de Meyer, mais quand on a besoin d'une référence rapide de modèles de design, existe-t-il un livre qui documente les implémentations sous leur forme la plus acceptée? – levelont

+0

@levelont Pas sûr mais [ici] (http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) est l'endroit idéal pour commencer votre recherche – NathanOliver