1.添加输入密码的保密性
LoginWidget::LoginWidget(QDialog*parent)
: QDialog(parent)
{
ui.setupUi(this);
ui.PassWord_Edit->setEchoMode(QLineEdit::Password);
BindSlots();
}
2.添加密码的验证提示
3.修复内存泄漏,并嵌套UI子窗口到主窗口里面
之前并没有设置窗口的父类对象,而选择在mainWindow的析构函数释放函数导致的一系列问题
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
_LoginWidget = new LoginWidget(this);
_RegisterWidget = new RegisterWidget(this);
setCentralWidget(_LoginWidget);
_LoginWidget->setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
_RegisterWidget->setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint);
_RegisterWidget->hide();
// 绑定槽函数
BindSlots();
}
4.设置错误提示的文本颜色
修改.qss文件
#Tip_Label[state='normal']
{
color: green;
}
#Tip_Label[state='err']
{
color: red;
}
5.创建全局头文件,来管理状态
刷新当前UI窗口的状态,去qss文件中查询当前的窗口的样式
#ifndef GLOBAL_H
#define GLOBAL_H
#include <QWidget>
#include <functional>
#include "QStyle"
// extern 声明此变量是在其他文件中定义的全局变量
extern std::function<void(QWidget*)> repolish;
#endif // GLOBAL_H
#include "Global.h"
// 初始化声明的全局变量
std::function<void(QWidget*)> repolish = [](QWidget* Widget)
{
Widget->style()->unpolish(Widget);
Widget->style()->polish(Widget);
};
6.刷新窗口的样式
#include "Global.h"
RegisterWidget::RegisterWidget(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
ui.Tip_Label->setProperty("state", "normal");
repolish(ui.Tip_Label);
}
7.正则表达式解析验证码
void RegisterWidget::ShowTipLabel(const QString& tip, QString State)
{
ui.Tip_Label->setText(tip);
ui.Tip_Label->setProperty("state", State);
repolish(ui.Tip_Label);
}
void RegisterWidget::OnGetCodeButtonClicked()
{
QString Email = ui.Email_Edit->text();
// 设置正则表达式
QRegularExpression Regex(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)");
bool Match = Regex.match(Email).hasMatch();
if (Match)
{
// 发送验证码
}
else
{
ShowTipLabel(QString::fromLocal8Bit("邮箱格式不正确"), "error");
}
}
8.创建模板单例类
#ifndef SINGLETON_H
#define SINGLETON_H
#include "Global.h"
template<typename T>
class Singleton
{
protected:
Singleton<T>() = default;
Singleton(const Singleton<T>& ) = delete;
Singleton<T>& operator=(const Singleton<T>& ) = delete;
static std::shared_ptr<T> m_pInstance;
public:
static std::shared_ptr<T> Instance()
{
static std::once_flag flag;
std::call_once(flag, []() {
try {
m_pInstance = std::shared_ptr<T>(new T());
}
catch (...) {
// 处理异常,这里可以记录日志等
std::cerr << "Failed to create singleton instance." << std::endl;
}
});
return m_pInstance;
}
void PrintAddress()
{
cout << "Singleton<T> address: " << m_pInstance.get() << endl;
}
~Singleton<T>()
{
std::cout << "Singleton<T> is destroyed" << std::endl;
}
};
template<typename T>
std::shared_ptr<T> Singleton<T>::m_pInstance = nullptr;
#endif // SINGLETON_H
9.创建全局唯一的模板网络管理类
头文件报错,模块没有添加,添加模块
#ifndef HTTPMANAGER_H
#define HTTPMANAGER_H
#include "singletion.h"
#include <QString>
#include <QUrl>
#include <QObject>
#include <QNetworkAccessManager>
#include <QJsonObject>
#include <QJsonDocument>
class HttpManager : public QObject, public Singleton<HttpManager>
{
Q_OBJECT
//public:
// ~HttpManager();
//
private:
HttpManager();
};
#endif // HTTPMANAGER_H
在别的地方获取该模板类的指针编译,报错误
报错是编辑器还没有反应过来,已经没有问题了,然而在模板类中会析构自己创建的智能指针,而这个智能指针又会去调用HttpManager的析构函数,所以必须要将析构函数的权限设置为公有
如果设置为私有智能指针就找不到这个析构函数就会编译报错
设置成公有就没有任何问题
10.封装发送HTTP请求的接口
/* 发送Http请求 */
void PostHttpReq(QUrl url, QJsonObject jsonObj, ReqId reqId, Modules module);
创建全局的枚举
signals:
void sig_http_finished(ReqID reqId, QString res, ErrorCodes errCode, Modules module);
11.Shared_Ptr的特点
每一个Shared_ptr 维护一个控制块,记录当前指向资源的引用计数(即有多少个shared_ptr 共享该资源)
行为:
构造/拷贝: 引用计数++
析构/重置(reset()) : 引用计数--
计数归零时自动释放该资源
#include <iostream>
#include <memory>
class Widget {
public:
Widget() {
std::cout << "Widget constructor run" << std::endl;
}
~Widget() {
std::cout << "Widget destructor run" << std::endl;
}
std::shared_ptr<Widget> GetSharedObject() {
return std::shared_ptr<Widget>(this);
}
};
int main()
{
std::shared_ptr<Widget> p(new Widget());
std::shared_ptr<Widget> q = p;
std::cout << p.use_count() << std::endl;
std::cout << q.use_count() << std::endl;
return 0;
}
智能指针拷贝或者赋值时,引用计数会++;
std::shared_ptr<Widget> p(new Widget());
std::shared_ptr<Widget> q = p->GetSharedObject();
int main()
{
Widget* w = new Widget();
std::shared_ptr<Widget> p(w);
std::shared_ptr<Widget> p2(w);
//std::shared_ptr<Widget> q = p->GetSharedObject();
std::cout << p.use_count() << std::endl;
std::cout << p2.use_count() << std::endl;
return 0;
}
这两段代码其实是等价的,这就为同一个对象创建了第二个控制块,导致他们的引用计数都为1,造成了二次析构
std::enable_shared_from_this正是为了解决这个问题而存在
12.std::enable_shared_from_this
使用条件极为苛刻,只有当该对象被Shared_Ptr管理时,才能够正常的使用,否则会报错
#include <iostream>
#include <memory>
class Widget : public std::enable_shared_from_this<Widget> {
public:
Widget() {
std::cout << "Widget constructor run" << std::endl;
}
~Widget() {
std::cout << "Widget destructor run" << std::endl;
}
std::shared_ptr<Widget> GetSharedObject() {
return shared_from_this();
}
};
int main()
{
Widget* w = new Widget();
//std::shared_ptr<Widget> p(w);
std::shared_ptr<Widget> q = w->GetSharedObject();
//std::cout << p.use_count() << std::endl;
std::cout << q.use_count() << std::endl;
return 0;
}
shared_from_this()
:- 返回与已有
shared_ptr
共享控制块 的新shared_ptr
。 - 若对象未被
shared_ptr
管理,调用它会抛出std::bad_weak_ptr
异常。
- 返回与已有
总结
- 永远不要直接通过
this
构造shared_ptr
,这会导致多个独立的控制块。 - 需要对象内部返回
shared_ptr
时,必须使用std::enable_shared_from_this
和shared_from_this()
。 - 创建对象时,优先使用
std::make_shared
或确保对象始终由shared_ptr
管理。
13.继续完善发送HTTP请求的接口
/* 发送HTTP请求 */
void HttpManager::PostHttpReq(QUrl url, QJsonObject jsonObj, ReqID reqId, Modules module)
{
QByteArray data = QJsonDocument(jsonObj).toJson();
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setHeader(QNetworkRequest::ContentLengthHeader, data.length());
std::shared_ptr<HttpManager> self = shared_from_this();
QNetworkReply* reply = m_pNetworkAccessManager->post(request, data);
connect(reply, &QNetworkReply::finished, [self, reply, reqId, module]()
{
if (reply->error() == QNetworkReply::NoError)
{
QString strReply = QString(reply->readAll());
emit self->sig_http_finished(reqId, strReply, ErrorCodes::SUCCESS, module);
reply->deleteLater();
return;
}
else
{
qDebug() << "HttpManager::PostHttpReq error: " << reply->error();
emit self->sig_http_finished(reqId, "", ErrorCodes::ERROR_NETWORK, module);
reply->deleteLater();
return;
}
});
}
14.创建http请求完成的槽函数
public slots:
void slot_http_finished(ReqID reqId, QString res, ErrorCodes errCode, Modules module);
/* 处理http请求发送成功的槽函数 */
void HttpManager::slot_http_finished(ReqID reqId, QString res, ErrorCodes errCode, Modules module)
{
/* 转发给注册模块 */
if (module == Modules::REGISTERMOD)
{
emit sig_reg_mod_finished(reqId, res, errCode);
}
}
15.实现注册模块的槽函数
/* 当注册成功时 */
void slot_reg_mod_finish(ReqID reqId, QString res, ErrorCodes errCode);
void RegisterWidget::BindSlots()
{
connect(ui.Get_Code_Btn, &QPushButton::clicked, this, &RegisterWidget::OnGetCodeButtonClicked);
/* 当注册请求完成时 */
connect(HttpManager::Instance().get(), &HttpManager::sig_reg_mod_finished, this, &RegisterWidget::slot_reg_mod_finish);
}
/* 注册模块回调 */
void RegisterWidget::slot_reg_mod_finish(ReqID reqId, QString res, ErrorCodes errCode)
{
if (errCode != ErrorCodes::SUCCESS)
{
ShowTipLabel(QString::fromLocal8Bit("注册失败"), "error");
return;
}
}
16.解析Json字符串(反序列化)
// 解析Json
void AnalyzingJsonData(ReqID reqId, QByteArray data);
/* 解析Json数据 */
void RegisterWidget::AnalyzingJsonData(ReqID reqId, QByteArray data)
{
QJsonDocument jsonDoc = QJsonDocument::fromJson(data); // .json文件
if (jsonDoc.isNull())
{
ShowTipLabel(QString::fromLocal8Bit("json 解析失败"), "error");
return;
}
// 解析json数据
if (!jsonDoc.isObject())
{
ShowTipLabel(QString::fromLocal8Bit("json 格式不正确"), "error");
return;
}
// 回调函数
_handlers[reqId](jsonDoc.object());
}
void RegisterWidget::InitHttpHandlers()
{
// 注册获取验证码回包的逻辑
_handlers.insert(ReqID::ID_GET_VARIFY_CODE, [this](const QJsonObject& jsonObj)
{
int error = jsonObj["error"].toInt();
if (error != ErrorCodes::SUCCESS)
{
ShowTipLabel(QString::fromLocal8Bit("参数错误"), "error");
return;
}
auto email = jsonObj["email"].toString();
ShowTipLabel(QString::fromLocal8Bit("验证码已发送至邮箱,请注意查找"), "normal");
qDebug() << QString::fromLocal8Bit("验证码已发送至邮箱") << email;
});
}
注册函数对象,来动态绑定回调