C++ 难以在单例类中创建类的实例

Posted

技术标签:

【中文标题】C++ 难以在单例类中创建类的实例【英文标题】:C++ Difficulty Creating Instance of Class within Singleton Class 【发布时间】:2019-08-19 21:29:42 【问题描述】:

我有一个相当不错的模板(如在代码的 sn-p 中),每当我需要一个单例类时,我就会退出。我现在正尝试在我的项目中应用它,以允许我控制 Web 服务器的单个实例。我可以制作一个 Web 服务器,而无需将其封装在我的课堂上。当我试图将它包含在课堂上时,我显然太不熟练了,无法将其完成。

我在这里尝试了明显的谷歌搜索。我已经阅读了相关的帖子。我确信这并不意味着我有一个独特的问题,只是我还没有找到解决问题的正确方法。这是我正在使用的:

webserver.h:

#include <ESP8266WebServer.h>
#include <FS.h>

class WebServer 
    private:
        // Singleton Declarations
        static bool instanceFlag;
        static WebServer *single;
        WebServer() 
        // Other Declarations
        FS *filesystem;
        ESP8266WebServer server();
        String getContentType(String);
        bool handleFileRead(String);

    public:
        // Singleton Declarations
        static WebServer* getInstance();
        ~WebServer() instanceFlag = false;
        // Other Declarations
        void initialize(int);
        void handleLoop();
;

webserver.cpp:

#include "webserver.h"

bool WebServer::instanceFlag = false;
WebServer* WebServer::single = NULL;

WebServer* WebServer::getInstance() 
    if(!instanceFlag) 
        single = new WebServer();
        instanceFlag = true;
        return single;
     else 
        return single;
    


void WebServer::initialize (int port) 
    ESP8266WebServer server(port);
    FS *filesystem;
    filesystem->begin();

    Serial.print("Open: http://");
    Serial.print(WiFi.hostname().c_str());
    Serial.println(".local");

    server.onNotFound([]() 
        if (!single->handleFileRead(single->server.uri())) 
            single->server.send(404, "text/plain", "404: File not found.");
        
    );

    server.begin();
    Serial.print("HTTP server started on port ");
    Serial.print(port);
    Serial.println(".");


String WebServer::getContentType(String filename) 
    if (single->server.hasArg("download")) 
        return "application/octet-stream";
     else if (filename.endsWith(".htm")) 
        return "text/html";
     else if (filename.endsWith(".html")) 
        return "text/html";
     else if (filename.endsWith(".css")) 
        return "text/css";
     else if (filename.endsWith(".js")) 
        return "application/javascript";
     else if (filename.endsWith(".png")) 
        return "image/png";
     else if (filename.endsWith(".gif")) 
        return "image/gif";
     else if (filename.endsWith(".jpg")) 
        return "image/jpeg";
     else if (filename.endsWith(".ico")) 
        return "image/x-icon";
     else if (filename.endsWith(".xml")) 
        return "text/xml";
     else if (filename.endsWith(".pdf")) 
        return "application/x-pdf";
     else if (filename.endsWith(".zip")) 
        return "application/x-zip";
     else if (filename.endsWith(".gz")) 
        return "application/x-gzip";
     else 
       return "text/plain"; 
    


bool WebServer::handleFileRead(String path) 
    Serial.println("handleFileRead: " + path);
    if (path.endsWith("/")) 
        path += "index.htm";
    
    String contentType = getContentType(path);
    String pathWithGz = path + ".gz";
    if (filesystem->exists(pathWithGz) || filesystem->exists(path)) 
        if (filesystem->exists(pathWithGz)) 
            path += ".gz";
        
        File file = filesystem->open(path, "r");
        single->server.streamFile(file, contentType);
        file.close();
        return true;
    
    return false;


void WebServer::handleLoop() 
    single->server.handleClient();

我得到的错误都类似于以下内容:

src\webserver.cpp: In member function 'bool WebServer::handleFileRead(String)':
src\webserver.cpp:81:23: error: 'WebServer::single->WebServer::server' does not have class type
         single->server.streamFile(file, contentType);

我得到“没有类类型”的想法,我只是不知道这意味着什么。在我看来,“single”是指向该类的指针,所以我不清楚该引用是什么不起作用。

显然,有大量示例如何在不封装 Web 服务器的情况下创建它。我需要为这个项目做的其他事情有助于创建该需求。

【问题讨论】:

FWIW,如果你要制作单例,你应该使用Meyers' Singleton WebServer::server 看起来很像一个函数:ESP8266WebServer server(); 你确定你想要那些括号,还是我们应该把它当作错字关闭? @user4581301,不确定您的确切意思。我看到的唯一括号在 server.onNotFound 处理程序中,当我删除它时,我遇到了同样的问题。我确定 'WebServer::single->WebServer::server' 部分正在用我需要的信息向我尖叫,我猜我只是不明白。 感谢您的参考@NathanOliver。我会看看,虽然我怀疑实例化/使用 lib 类会犯同样的错误。 道歉不够清楚。 ESP8266WebServer server(); 末尾的括号意外地声明了一个函数而不是定义了一个变量,从而导致了很久以后的错误消息。函数不是类,因此您不能使用. 访问成员。您会在编程中发现的一件事是,实际的错误通常不在崩溃现场附近。 【参考方案1】:

您的代码中有一些错误。 在webserver.h

...
private:
    // Singleton Declarations
    static bool instanceFlag;
    static WebServer *single;
    WebServer() 
    // Other Declarations
    FS *filesystem;
    ESP8266WebServer *server; // <--- remove the parentheses and make it a pointer
    String getContentType(String);
    bool handleFileRead(String);
...

webserver.cpp:

WebServer::initialize 我猜你想初始化类serverfilesystem 而不是本地,所以它应该看起来像这样:

void WebServer::initialize (int port) 
    server = new ESP8266WebServer(port);
    filesystem = new FS();
    ...

现在,无论您在哪里使用服务器,都必须使用 -&gt; 运算符。 例如:

void WebServer::handleLoop() 
    single->server->handleClient();

请记住,serverfilesystem 对象必须被删除以避免内存泄漏。

编辑:

你得到了新的错误,因为 FS 没有没有参数的构造函数。 FS的构造函数是这样的:FS(FSImplPtr impl) : _impl(impl) ,here你可以看到FSImplPtr是std::shared_ptr&lt;FileImpl&gt;的typedef,所以你需要提供这个作为参数。

它按你的方式工作,因为 SPIFFS 的存在被声明为 here 并且是 FS 类型。

如果你想使用SPIFFS,你必须像这样使用它:filesystem = &amp;SPIFFS;,不像你在 cmets (FS* filesystem = &amp;SPIFFS;) 中提到的那样,因为你的方式创建了一个名为 filesystem 的新临时变量,并且可能您希望在课堂上发起filesystem,而不是在本地发起。

【讨论】:

感谢您对@N Alex 所看到的问题的简明审查。我会试试看的。 所以这绝对是我声明 ESP8266WebServer 的问题 - 不知道我是如何忽略 () 那里的。也许如果我下次注意 IntelliSense?现在在WebServer::initialize 中出现错误:src\webserver.cpp:18:25: error: no matching function for call to 'fs::FS::FS()' 不确定声明 filesystem = new FS(); 有什么问题?我真诚地希望这不是我犯的另一个愚蠢的错误。我将其更改为 FS* filesystem = &SPIFFS; - 现在测试看看是否符合我的预期。 所以你让我克服了我发布的错误 - 谢谢!虽然我很好奇为什么我需要将 FS 声明为FS* filesystem = &amp;SPIFFS; 以及在WebServer::initialize 中以相同的方式实例化? @LCB 请查看我的编辑以获取新错误的答案 我现在明白了,非常感谢@N_Alex 的帮助。

以上是关于C++ 难以在单例类中创建类的实例的主要内容,如果未能解决你的问题,请参考以下文章

使用python中的构造函数限制在单例类中创建对象

如何快速创建单例类? [复制]

C++ 单例模式

在多处理中创建单例类

是否需要在单例类中使用弱引用?

单例模式