首页 > 解决方案 > 为什么我的单例实现不能编译?(C++)

问题描述

我是 C/C++ 新手

我试图实现单例模式。

为什么不遵循代码无法编译。在 javascript 中,以类似的方式实现单例模式没有问题。(https://addyosmani.com/resources/essentialjsdesignpatterns/book/#singletonpatternjavascript

我认为当我声明指针时*singleton_instance static,它可以通过静态方法访问。但是 G++ 给出了错误

singleton_pattern_nullptr.cpp:12:20: warning: ‘constexpr’ needed for in-class initialization of static data member ‘Grammar* Grammar::singleton_instance’ of non-integral type [-fpermissive]
   12 |    static Grammar *singleton_instance = NULL;

但即使有

#include <iostream>
#include <stdio.h>    
#define NULL nullptr
    
class Grammar {         
protected:
    Grammar() {};
    Grammar(const Grammar&) = delete;
    Grammar& operator=(const Grammar&) = delete;

    static Grammar *singleton_instance = NULL;          
public:         
    Grammar& getInstance();     
}; 

static Grammar& Grammar::getInstance() {
    if(singleton_instance == NULL) {
        // no star with new
        singleton_instance = new Grammar();
    }           
    return *singleton_instance;
}
    
int main(int argc, char* argv[]) {          
    Grammar *grammar;       
    grammar = Grammar::getInstance();
            
    return 0;
}

现在当我定义

static constexpr Grammar *singleton_instance = NULL;

g ++产生错误消息...

singleton_pattern_nullptr.cpp:23:23: error: assignment of read-only variable ‘Grammar::singleton_instance’
   23 |    singleton_instance = new Grammar();

还是 ...

singleton_pattern_nullptr.cpp:33:34: error: cannot call member function ‘Grammar& Grammar::getInstance()’ without object
   33 |   grammar = Grammar::getInstance();

我以为我可以做到,mutable但在这个线程中(C++ 中的静态可变成员变量?)我读到不需要指定静态成员变量才能成为mutable(可变),因为这应该已经是无论如何。

所以请告诉我我的错误在哪里,以及在 C++ 中是否可以实现我上面想到的单例(参见 javascript 示例)。

谢谢

编辑:

在承认了目前的答案之后,我仍然无法判断我做错了什么。请告诉我如何更改定义指针*singleton_instanceGrammar::getInstance()以便我可以根据想法操作指针,如果可能的话。我现在沮丧地声明了静态,并根据 g++ 的错误消息用*singleton_instance修饰符声明。constexpr

不会那么难吧?

另一个例子:https ://developer-blog.net/singleton-design-pattern-in-c/

谢谢

编辑2:

感谢M47发布有效的“Meyers Singleton”(用户idclev 463035818注释)

但是,尽管我设置了标志-std=c++17,编译器仍然会以警告响应

**singleton_pattern.h:9:12:** warning: inline variables are only available with ‘-std=c++17’ or ‘-std=gnu++17’
    9 |     static inline Grammar *singleton_instance = nullptr;

但是请允许我提出三个(两个?)关于 c++ 的更多理解问题。

1 - 在头文件中,方法声明static inline

static inline Grammar *singleton_instance = nullptr;

但是,在定义中,您传递了这些“修饰符” (?)。或者是通过命名空间/类操作符在定义中隐含一个static或两个?我想我在某处读过类似的东西。static inline::

Grammar& Grammar::getInstance()

如果不是这样,那么我知道头文件声明和 cpp 文件定义中的分歧修饰符上有线程。你能给我一个提示吗?

最后但并非最不重要的(小问题)我收到警告

warning: variable ‘grammar’ set but not used [-Wunused-but-set-variable]

非常感谢您向我展示了如何使单例模式的指针版本工作!赞!

标签: c++design-patternsstatic

解决方案


无需将静态实例显式声明为成员变量。你也可以这样做:

class Grammar
{
public:
    Grammar() {}
    Grammar(const Grammar&) = delete;
    Grammar& operator=(const Grammar&) = delete;
    static Grammar& Grammar::getInstance() {
        static Grammar instance;
        return instance
    }
};

这样,您还可以避免直接使用原始指针、潜在的分段错误和 SIOF。话虽如此,根据C++ 核心指南不推荐使用单例范式。

请注意,我已经删除了前两行中的复制构造函数和赋值。这通常在单例范式中完成,以避免不需要的副本。

编辑:用指针做

// header file:
class Grammar {
protected:
    Grammar() {};
    Grammar(const Grammar&) = delete;
    Grammar& operator=(const Grammar&) = delete;

    static inline Grammar *singleton_instance = nullptr;   
public:
    static Grammar& getInstance();
};
// cpp file
Grammar& Grammar::getInstance() {
    if (singleton_instance == nullptr) {
        // no star with new
        singleton_instance = new Grammar();
    }
    return *singleton_instance;
}
// main.cpp
#include "grammar.h"
int main(int argc, char* argv[]) {
    Grammar *grammar;
    grammar = &Grammar::getInstance();

    return 0;
}

静态内联成员变量仅在 C++17 中定义。另一种方法是在类外初始化静态成员。它必须在一个且只有一个翻译单元中完成。这可能会导致 SIOF:

// cpp file
#include "grammar.h"
Grammar* Grammar::singleton_instance = nullptr;
...

推荐阅读