1.CRTR实现Http管理者

模板的单例模式

template<typename T>
class Singleton{
protected:
Singleton() = default;
Singleton(const Singleton<T> &) = deltete;
Singleton& operator=(const Singleton<T> &) = delete;
static std::shared_ptr<T> _instance;
   
public:
	static std::shared_ptr<T>  Getinstance(){
        static std::once_flag s_flag;
        std::call_once(s_flag,[&](){
            _instance = std::shared_ptr<T>(new T);
        });
        return _instance;
    }
    void PrintAddress(){
        std::cout << _instance.get()<< std::endl;
    }

    ~Singleton(){
        std::cout << " this is singleton destruct " << std::endl;
    }
};
template<typename T>
std::shared_ptr<T> Singleton<T>::_instance = nullptr;
static std::once_flag s_flag;表示一段代码只运行一次的标志
call_once确保在多线程环境下只调用一次

知识点

1.在单例模式中将构造函数放入protected中是为了当子类继承该类时能够构造基类

2.在单例类中,不希望手动回收单例类的资源,可以将静态变量设置为智能指针的形式

3.对于智能指针,为什么使用new一个对象调用智能指针的构造函数来初始化而不是使用make_shared?因为make_shared需要访问类的构造函数,但是这个单例类的构造函数是protected的无法访问

4.使用make_shared的缺点:make_shared会在一个内存块中同时分配对象和控制块,导致即使所有shared_ptr实例都已经销毁,由于如std::weak_ptr的其他引用,内存块可能仍未释放

5.对于类的静态成员变量,需要在.cpp中实例化,对于模板类的静态成员变量,需要在.h中实例化

6.Http是基于tcp实现的,但是http发送请求后收到回复就会断开连接,不会去维护一个长效的连接,所以Http不用去切包(不存在粘包的问题,因为Http头部会设置一些对端需要获取的信息:发送的数据的类型(json,txt之类的),发送的数据的总长度(对方就会获取到这么多长度的数据再去处理:底层去切包组合)

7.使用QByteArray的函数会解决大端小端的问题

8.shared_from_this()是通过类自己的this指针来生成自己的智能对象

9.CRTP是c++的一种设计模式,使用模版的特性来实现静态多态,CRTP 的核心思想是一个类通过模板参数引用自身,从而在编译时实现某种行为的复用。

template <typename Derived>
class Base {
public:
    void interface() {
        // 可以调用 Derived 的方法
        static_cast<Derived*>(this)->implementation();
    }

    // 其他基类方法...
};

class Derived : public Base<Derived> {
public:
    void implementation() {
        // 实现具体的功能
    }
};

Base 是一个模板类,它的模板参数是 Derived,也就是派生类。
Derived 继承自 Base<Derived>,这使得 Base 可以通过 static_cast 调用 Derived 的具体实现。
  1. 静态多态:CRTP 允许在编译时确定调用的函数,而不是在运行时。这种静态多态的特性比动态多态(如虚函数)更高效。
  2. 代码复用:可以在基类中实现通用的功能,而具体的实现留给派生类,减少了重复代码。
  3. 类型安全:由于 CRTP 是在编译时解析的,因此它比某些其他的多态实现(如使用 void* 或基类指针)更安全。

CRTP 的应用场景

  • 实现接口:可以用于定义接口,并在派生类中实现具体的行为。
  • 类型特征:可以用于实现一些类型特征(如 enable_if 或其他类型萃取)。
  • 策略模式:可以用于实现策略模式,通过 CRTP 将策略的实现绑定到基类。

C++ 编译器对 CRTP 的支持

C++ 编译器支持 CRTP 的原因主要与 C++ 的模板机制有关。C++ 的模板系统允许在编译时生成代码,这使得 CRTP 成为可能。具体来说:

  1. 模板实例化:编译器在编译时根据模板参数生成具体的类,这使得 CRTP 能够在编译时解析类型信息。
  2. 类型检查:由于 CRTP 是基于模板的,编译器能够在实例化时进行类型检查,确保类型的安全性。
  3. 优化:编译器可以进行更好的优化,因为在编译时就知道了所有类型的信息。这使得使用 CRTP 的代码在性能上通常优于使用虚函数的代码。
template <typename Derived>
class Counter {
public:
    void increment() {
        static_cast<Derived*>(this)->do_increment();
    }
    void print() const {
        std::cout << "Count: " << static_cast<const Derived*>(this)->count() << std::endl;
    }
};
class MyCounter : public Counter<MyCounter> {
public:
    MyCounter() : count_(0) {}

    void do_increment() {
        ++count_;
    }
    int count() const {
        return count_;
    }
private:
    int count_;
};
int main() {
    MyCounter counter;
    counter.increment();
    counter.increment();
    counter.print(); // 输出: Count: 2
    return 0;
}

实现

// CRTP奇异递归模板
class HttpMgr : public QObject ,public Singleton<HttpMgr> ,
        public std::enable_shared_from_this<HttpMgr>
{
    Q_OBJECT
public:
    ~HttpMgr();

private:
    friend class Singleton<HttpMgr>;// 使用友元是为了让父类能够调用子类的构造函数去构造单例
    QNetworkAccessManager _manager;

private:
    HttpMgr();
    void PostHttpReq(QUrl url, QJsonObject json , ReqId req_id , Modules mod); // json req_id是功能 modules是模块

private slots:
    void slot_http_finish(ReqId id , QString res , ErrorCodes err ,Modules mod);

signals:
    void sig_http_finish(ReqId id , QString res , ErrorCodes err ,Modules mod);// 当一个Http发送完成之后,会发送该信号给其他模块做界面上的显示等功能
    void sig_reg_mod_finish(ReqId id , QString res , ErrorCodes err);
};
#include "httpmgr.h"

HttpMgr::~HttpMgr()
{

}

HttpMgr::HttpMgr()
{
    connect(this,&HttpMgr::sig_http_finish,this,&HttpMgr::slot_http_finish);
}

void HttpMgr::PostHttpReq(QUrl url, QJsonObject json, ReqId req_id, Modules mod)
{
    // 将json对象序列化为字节数组
    QByteArray data = QJsonDocument(json).toJson();
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
    // 使用QByteArray的函数会解决大端小端的问题
    request.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.length()));
    auto self = shared_from_this();//35:20
    // 异步发送请求
    QNetworkReply * reply = _manager.post(request,data);
    // 判读是否完成
    QObject::connect(reply,&QNetworkReply::finished,[self,reply,req_id,mod](){// 为什么使用self而不是使用this 41:00
        // 处理错误情况
        if(reply->error() != QNetworkReply::NoError)
        {
            qDebug() << reply->errorString();
            // 发生信号给相关界面完成
            emit self->sig_http_finish(req_id,"",ErrorCodes::ERR_NETWORK,mod);
            // 因为reply还有可能用于底层的数据循环,使用deleteLater告诉QT不需要使用时回收
            reply->deleteLater();
            return;
        }

        // 无错误
        QString res = reply->readAll();
        // 发送信号通知完成
        emit self->sig_http_finish(req_id,res,ErrorCodes::SUCCESS,mod);
        reply->deleteLater();
        return;
    });

}

void HttpMgr::slot_http_finish(ReqId id, QString res, ErrorCodes err, Modules mod)
{
    if(mod == Modules::REGISTERMOD)
    {
        // 发送信号通知指定模块http的响应结束了
        emit sig_reg_mod_finish(id,res,err);
    }
}