无法在单例类析构函数中访问私有成员

4

我正在尝试实现这个单例类。但我遇到了这个错误:

'Singleton ::〜Singleton':无法访问在类'Singleton'中声明的私有成员 这在头文件中被标记,最后一行包含闭括号。

有人能帮我解释一下这个问题是什么原因吗? 以下是我的源代码。

Singleton.h:


class Singleton
{
public:
    static Singleton* Instance()
    {
        if( !pInstance )
        {
            if( destroyed )
            {
                // throw exception
            }
            else
            {
                Create();
            }

        }
        return pInstance;
    }
private:
    static void Create()
    {
        static Singleton myInstance;
        pInstance = &myInstance
    }
    Singleton() {}
    Singleton( const Singleton& );
    Singleton& operator=( const Singleton& );
    ~Singleton() 
    {
        pInstance = 0;
        detroyed = false;
    }

    static Singleton* pInstance;
    static bool destroyed;
};

Singleton.cpp:


Singleton* Singleton::pInstance = 0;
bool Singleton::destroyed = false;

在我的主函数中:


Singleton* s = Singleton::Instance();

如果我将析构函数设置为public,则问题会消失。但是一本书(《现代C++设计》)说应该将其设置为private,以防止用户删除实例。实际上,我需要在析构函数中放置一些清理pInstance和destroyed的代码。
顺便说一下,我正在使用Visual C++ 6.0进行编译。

我认为它应该可以工作,你能否提供代码以便查看错误? - Ashish
1
@Mac:代码闪烁?我不确定我理解这是什么意思。 - jasonline
Neeraj:是吗?那我猜这只会在Visual C++中发生了?! - jasonline
我在想可能是由于静态对象的创建吗?程序结束后,它需要调用析构函数。但是由于析构函数是私有的... - jasonline
1
如果那真的是某种奇怪的VC6错误,并且您不需要在析构函数中执行任何行为,那么您可以使用static Singleton * instance = new Singleton; - 或者只是不使用单例模式并使用返回所需实例的自由函数。 - Georg Fritzsche
显示剩余5条评论
5个回答

2

您发布的代码看起来没有问题,因此问题必须在源代码的其他部分。

错误消息将在发生问题的文件名和行号之前显示。请查看该行,您将看到一些代码,该代码要么尝试调用单例指针上的delete,要么尝试构造单例实例。

错误消息将类似于以下内容(文件和行号仅为示例):

c:\path\to\file.cpp(41) : error C2248: 'Singleton::~Singleton': cannot access private member declared in class 'Singleton'

因此,在这种情况下,您需要查看file.cpp中第41行发生了什么。


它实际上指向头文件,在文件末尾(闭合括号)。而这些是我工作区中唯一的源代码。 - jasonline
这是在编译singleton.cpp还是其他源文件时发生的?#include "singleton.h"之前的代码是什么? - R Samuel Klatchko
@R Samuel Klatchko:正在编译singleton.cpp。这只是stdafx.h的另一个包含文件。已经有人在发布的答案中成功复现了它。 - jasonline

2
您应该告诉我们您正在使用的Visual C ++版本是VC6。我可以重现该错误。
此时,我没有建议,除非尽可能升级到更高版本的MSVC(Express版中可免费获得VC 2008)。
还有几个数据点- VC2003及更高版本不会像您的示例中那样将Singleton析构函数设置为私有。

@Michael:对不起,我漏掉了那个信息。感谢你的提供的信息。 - jasonline
VC6现在相当古老 - 如果你正在维护为它编写的代码,使用它是有意义的,但对于新项目,请使用更现代的工具。这样做可以避免很多不必要的麻烦(C ++中已经有足够的引发头痛的东西了,如果没有必要,就不要再用VC6增加麻烦了)。 - Michael Burr
是的,我正在维护旧代码,所以我想尝试在这个环境中编译。不幸的是,它没有起作用。我想我只能在其他编译器上尝试了。 - jasonline
如果你必须使用VC6,或许只能接受dtor是公共的。 - Michael Burr

2

我不是C++或VC专家,但你的例子看起来与这个页面描述的那个类似...作者称其为编译器错误。


@Stephen:非常感谢您提供的信息。您的链接提供了一系列搜索结果。我浏览过了,我认为是这个 - http://www.codeguru.com/forum/archive/index.php/t-236067.html。 - jasonline

0

个人而言,除非我在使用模板单例类,否则我不会在我的单例中放置析构函数,但是那时我会将它们设置为受保护的。

template<class T>
class Singleton
{
public:
    static T &GetInstance( void )
    {
        static T obj;
        return obj;
    }

    static T *GetInstancePtr( void )
    {
        return &(GetInstance());
    }

protected:
    virtual ~Singleton(){};
    Singleton(){};

};

那么我将编写我的类如下:

class LogWriter : public Singleton<LogWriter>
{
friend class Singleton<LogWriter>;
}

@Craig:谢谢分享。实际上我需要析构函数来进行一些清理工作。这就是为什么。如果我将其设置为protected,我会得到相同的错误,即无法访问保护成员声明... - jasonline

-1
class Singleton
{
     static Singleton *pInstance = NULL;  
     Singleton(){};

     public:

    static Singleton * GetInstance() {

          if(!pInstance)
          {
               pInstance = new Singleton();
          }
          return pInstance;
     }

     static void  RemoveInstance() {

          if(pInstance)
          {
               delete pInstance;
               pInstance = NULL;
          }

     }
};

@Mac:谢谢分享。但是,使用这个方法,你实际上需要调用者调用RemoveInstance()来进行清理。我在考虑一种不需要要求调用者调用清理函数的实现方式。 - jasonline
这是编写单例模式的标准反模式。请学习正确(或者我应该说更好的)方法。Scott Myers有一些非常好的例子。 - Martin York

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接