【问题标题】:Is this the example of singleton pattern?这是单例模式的例子吗?
【发布时间】:2026-02-05 15:30:01
【问题描述】:

这是单例模式的例子吗?如果不是,那么如果我们将此类用作记录器,可能会出现什么问题。 (当然它不是一个完全灵活的记录器)

#include <iostream>
#include <fstream>
using namespace std;


class logger
{
    private:
        static ofstream myfile;

        void openfile()
        {
            myfile.open ("example.txt");
        }
        void closefile()
        {
            myfile.close();
        }
    public:     
        void logit(string str)
        {
            if (myfile.is_open() == 1)
            {
                myfile << str << endl;
            }
            else
            {   
                openfile();
                myfile << str << endl;
            }   
        }
};

ofstream logger::myfile;
int main () 
{
    logger l;
    l.logit ("log from vod application");
    logger l2;
            l.logit ("log from guide application");

    logger l3;
    l1.logit ("log from application 3-1");
    l1.logit ("log from application 3-2");

            return 0;
}

任何讨论都会有所帮助。

开发

【问题讨论】:

  • 不,不是因为它有一个公共构造函数
  • 这不是单例,您可能遇到的问题是,如果在问题运行时文件被删除/移动/以其他方式被篡改,它将不会被重新创建。但这个问题与它的单例性或缺乏性没有太大关系。
  • 这或多或少是 Meyer 版本的单例,其中唯一的实例是 myfile,并且您没有 getInstance 方法,而是直接访问 logit()。如果logit是静态的会更好
  • @MichaelKrelin 因此,即使它不是单例类,我们也可以将其用于日志记录而不会出现重大问题。有很多讨论建议您应该将单例用于记录器?如果将单例用于记录器,如果我们可以在没有单例的情况下实现相同的目标,我们究竟能得到什么?
  • @DeveshAgrawal,谁在乎它是否符合模式?它解决了单例应该解决的问题。虽然为什么要让 logit 在这种情况下成为非静态的,但我无法理解。

标签: c++ logging singleton


【解决方案1】:

正如其他人所说,它不是单例。它是一个 Monostate:http://c2.com/cgi/wiki?MonostatePattern,它是相关但不同的。

【讨论】:

    【解决方案2】:

    不,这不是单例。

    要创建单例,您必须将构造函数设为私有。您的类没有声明任何构造函数,因此,编译器将生成默认构造函数。这很糟糕,因为会有一个复制构造函数和赋值运算符,它们只是逐位复制所有成员。如果您复制已打开文件的句柄或指向已分配内存的指针并尝试使用副本进行操作,通常会发生不好的事情。

    class logger {
        static logger* the_logger;
        // other private members
    
        logger() : the_logger(NULL)
            { /*logger construction*/} 
    public:
        static logger* logger::instance(const string& filename) {
            if (the_logger == NULL) {
                the_logger = new logger(/*arguments*/);
            }
            return the_logger;
        }
    
        static void cleanup(void) {
            delete the_logger;
        }
        /* other public members*/
        void debug(...)
    }
    
    
    int main(void) 
    {
        logger::instance()->debug(blah-blah-blah);
        logger::cleanup();
    }
    

    为简单起见,我跳过了与同时访问共享资源(文件描述符或输出流)相关的代码。

    另外,如果我没记错的话,你的代码不会编译,因为静态成员只能用静态成员函数访问。

    【讨论】:

    • 这不是在 C++ 中实现单例反模式的最佳方式。使用static logger&amp; logger::instance() { static logger the_logger(/*arguments*/); return logger; }
    • 如果我想调整实例的参数怎么办?在提议的实现中,它被声明为函数内的静态变量。
    • 然后将它们作为参数传递。关键是您不应该使用new 或指针。 static logger&amp; logger::instance(std::string const&amp; filename) { static logger the_logger(filename); return logger; }.
    • 这是一个非常糟糕的 C++ 中的单例实现(特别是因为您必须手动删除它(PS 清理应该将 the_logger 设置为 NULL 以便下次它可以创建一个新的))。如需更好的设计,请参阅*.com/a/1008289/14065
    【解决方案3】:

    不可能是单例:

    1. 您的代码中有 3 个实例
    2. 该类有一个公共构造函数,可以简单地创建一个新实例

    【讨论】:

    • 可以单例就好了。它只是不遵守单例模式,这确保它必须是单例:-)
    • 那么它是单例的包装器 (logger::myfile)
    • logger:myfile 也不是单例:单例模式将类限制为单个实例,并提供对该实例的全局访问。
    • @Pete,是的,你是对的。我的意思是包装必须是单例的东西。 logger::myfileofstream