C ++ Singleton创建问题

时间:2009-06-23 20:29:13

标签: c++

我最近获得了一些我希望移植到Linux的代码。但是在一个头文件中,我有一些好奇的代码,我希望有人可以解释一下。在头文件中,在定义其他类的命名空间内,我有以下内容:

#define CREATE_SINGLETON_METHODS(s) \
  private: \
    friend class Singleton<c>; \
    ##c(); \
    virtual ~##c();

我理解##是令牌粘贴操作,但我无法弄清楚为什么原作者(我不知道,也无法联系)使用它。我有一个看起来像这样的实现类:

class MapManager : public Singleton<MapManager> {
CREATE_SINGLETON_METHODS(MapManager)

private:
...

编译时,我收到以下错误:

error: pasting ";" and "MapManager" does not give a valid preprocessing token

这会在Windows和一些早期版本的gcc(pre 4.x)上编译find。关于这里可能会发生什么的任何想法?谢谢!

8 个答案:

答案 0 :(得分:2)

我使用自己的通用单例模板: 尝试用这个模板替换那些icky宏:

#ifndef SINGLETON_HPP_STYLET
#define SINGLETON_HPP_STYLET 0


/*
*
*
*   Generic Singleton implementation
*
*
*/ 


        template <class T>
        class SingletonHolder : public T
        {
            public:                                  
                static SingletonHolder<T>& getInstance()
                {

                    static SingletonHolder<T> instance;
                    return instance;
                }

            private:
                SingletonHolder()
                {
                };

                virtual ~SingletonHolder()
                {
                };

        };//class SingletonHolder



#endif //SINGLETON_HPP_STYLET

<强> // --------------------------------------- ---------------------------

<强> USAGE:

class SomeClass;

typedef SingletonHolder<SomeClass> SomeClassSingleton;


SomeClassSingleton::getInstance().doSomething();

答案 1 :(得分:1)

在构造函数没用之前,我觉得像##一样。析构函数中的那个用于将波浪号粘贴到typename。也许是原作者复制粘贴,并没有得到编译错误,因为它是一个旧版本,所以没有发现错误。

答案 2 :(得分:1)

尝试将宏更改为:

#define CREATE_SINGLETON_METHODS(c) \
  private: \
    friend class Singleton<c>; \
    c(); // Note the change on this line! \
    virtual ~##c();

我拿出了一个##运算符。好像 ”;”正在与“c();”合并在gcc 4中。

答案 3 :(得分:1)

我总是发现这种预处理器“魔术”(读取垃圾)会造成比它值得更多的麻烦。在某些情况下,这是一个必要的邪恶,但我不认为这适用于此。我会在代码中重写它的任何实例,并消除那些可怕的事情。

答案 4 :(得分:1)

'stringizing'和令牌粘贴('#'和'##')的预处理程序运算符只能在宏参数上可靠地使用。此外,您通常需要使用间接级别来使它们在所有情况下都能正常工作(特别是在将它们用于本身就是宏的情况下)。

了解更多细节。

答案 5 :(得分:1)

前一段时间我遇到过类似的问题。尝试运行:

g ++ -E mytestfile.h

(当然使用您自己的文件名)在命令行上查看预处理器的结果。它可能有助于显示正在发生的事情。我正在运行gcc 3.4.4并获得您描述的仅在4.x中发生的相同错误(尽管代码在Visual Studio 2008中正常工作)。

消除令牌化器将解决问题:

#define CREATE_SINGLETON_METHODS(c) \
private: \
friend class Singleton<c>; \
c(); \
virtual ~c();

此代码为我提供了以下预处理器结果(使用-E选项从命令行复制):

private: friend class Singleton<MapManager>; MapManager(); virtual ~MapManager();

编译器很满意。

根据http://gcc.gnu.org/onlinedocs/gcc-4.0.4/cpp/Tokenization.html

  

一旦输入文件被分解为标记,标记边界就不会改变,除非使用“##”预处理运算符将标记粘贴在一起。   编译器不会重新标记预处理器的输出。每个预处理令牌都成为一个编译器令牌。

在这种情况下,

'##'会破坏标记化规则。根据你得到的错误,预处理器最有可能给编译器一个带有“; MapManager()”的令牌集,其中4.x编译器显然不喜欢。

此处的一些信息(http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html#Concatenation)也值得注意:

  

通常会发现不必要的用途   复杂宏中的'##'。如果你得到   这个警告,很可能是你   可以简单地删除'##'。

希望有所帮助。

答案 6 :(得分:0)

你是如何在任何编译器上编译的?首先它应该是:

#define CREATE_SINGLETON_METHODS(c) \
  private: \
    friend class Singleton<c>; \
    ##c(); \
    virtual ~##c();

因为我假设它是定义友元类的宏参数,私有构造函数和私有析构函数。

答案 7 :(得分:-1)

这看起来像是在自动单例创建方面做得不好。一般情况下,如果您可以使用虚拟函数或虚拟继承以及一些聪明的模板技巧,则可以不使用宏。

如果我是你,我会手动重写受影响的类的相关代码。对于像这样的东西,宏是坏消息。