C++ 难以在 Singleton Class 中创建 Class 的实例
C++ Difficulty Creating Instance of Class within Singleton Class
我有一个相当不错的模板(如代码片段),每当我需要一个单例时我就会退出 class。我现在正尝试在我的项目中应用它,以允许我控制 Web 服务器的单个实例。我可以制作一个 Web 服务器,而无需将其封装在我的 class 中。当我尝试将它包裹在 class 中时,我显然太不熟练而无法将其拉下来。
我在这里尝试了明显的谷歌搜索和搜索。我已经阅读了相关帖子。我确信这并不意味着我有一个独特的问题,只是我还没有找到解决它的正确方法。这是我正在处理的内容:
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);
我明白了 "does not have a class type" 的意思,只是不知道它在这里是什么意思。在我看来,"single" 是指向 class 的指针,所以我不清楚该引用不起作用。
显然,有很多示例可以说明如何在不封装的情况下构建 Web 服务器。我需要为这个项目做的其他事情有助于创建该需求。
你的代码有一些错误。
在 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
我猜你想初始化 class server
和 filesystem
而不是本地人,所以它应该看起来像这样:
void WebServer::initialize (int port) {
server = new ESP8266WebServer(port);
filesystem = new FS();
...
}
现在,无论您在何处使用服务器,都必须使用 ->
运算符。
例如:
void WebServer::handleLoop() {
single->server->handleClient();
}
请记住必须删除 server
和 filesystem
对象以避免内存泄漏。
编辑:
您收到新错误是因为 FS 没有不带参数的构造函数。
FS 的构造函数是这样的: FS(FSImplPtr impl) : _impl(impl) { }
, here 你可以看到 FSImplPtr 是 std::shared_ptr<FileImpl>
的类型定义,所以你需要提供它作为参数。
它按您的方式工作,因为 SPIFFS 的存在已声明 here 并且属于 FS 类型。
如果你想使用SPIFFS
,你必须像这样使用它:filesystem = &SPIFFS;
,而不是像你在评论中提到的那样(FS* filesystem = &SPIFFS;
),因为你的方式创建了一个新的名为 filesystem
的临时变量,您可能希望在 class 中启动 filesystem
,而不是本地变量。
我有一个相当不错的模板(如代码片段),每当我需要一个单例时我就会退出 class。我现在正尝试在我的项目中应用它,以允许我控制 Web 服务器的单个实例。我可以制作一个 Web 服务器,而无需将其封装在我的 class 中。当我尝试将它包裹在 class 中时,我显然太不熟练而无法将其拉下来。
我在这里尝试了明显的谷歌搜索和搜索。我已经阅读了相关帖子。我确信这并不意味着我有一个独特的问题,只是我还没有找到解决它的正确方法。这是我正在处理的内容:
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);
我明白了 "does not have a class type" 的意思,只是不知道它在这里是什么意思。在我看来,"single" 是指向 class 的指针,所以我不清楚该引用不起作用。
显然,有很多示例可以说明如何在不封装的情况下构建 Web 服务器。我需要为这个项目做的其他事情有助于创建该需求。
你的代码有一些错误。
在 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
我猜你想初始化 class server
和 filesystem
而不是本地人,所以它应该看起来像这样:
void WebServer::initialize (int port) {
server = new ESP8266WebServer(port);
filesystem = new FS();
...
}
现在,无论您在何处使用服务器,都必须使用 ->
运算符。
例如:
void WebServer::handleLoop() {
single->server->handleClient();
}
请记住必须删除 server
和 filesystem
对象以避免内存泄漏。
编辑:
您收到新错误是因为 FS 没有不带参数的构造函数。
FS 的构造函数是这样的: FS(FSImplPtr impl) : _impl(impl) { }
, here 你可以看到 FSImplPtr 是 std::shared_ptr<FileImpl>
的类型定义,所以你需要提供它作为参数。
它按您的方式工作,因为 SPIFFS 的存在已声明 here 并且属于 FS 类型。
如果你想使用SPIFFS
,你必须像这样使用它:filesystem = &SPIFFS;
,而不是像你在评论中提到的那样(FS* filesystem = &SPIFFS;
),因为你的方式创建了一个新的名为 filesystem
的临时变量,您可能希望在 class 中启动 filesystem
,而不是本地变量。