在做服务器时几点记录

1、在做通信协议的时候,使用了一个类NetProtocol来封装数据。为了使该类主持QDataStream,需要自定义友元>>和<<。

2、在该友元中,由于使用的是Json(公孙二狗的自定义json封装类),也需要在其中添加对于QDataStream的友元>>和<<。

3、在NetProtocol中,必须包含头文件<QtNetwork>,否则如果该类中带有基本类型的话,友元函数中是不认的,会报错:

//error: ambiguous overload for ‘operator<<’ (operand types are ‘QDataStream’ and ‘qint8’ {aka ‘signed char’})
//   |     ds <<request.requestType
//   |     ~~ ^~~~~~~~~~~~~~~~~~~~~
//   |     |            |
//   |     QDataStream  qint8 {aka signed char}

4、关键的,如果该类不做指针或引用传参的话,该类不能继承于QObject,因为QObject及其子类是没有拷贝构造函数的,QT的设计即是如此。实际上,这两者都被声明了,只不过它们使用了Q_DISABLE_COPY宏并在类的私有段声明的。QObject所有的直接子类和间接子类都没有拷贝构造函数和赋值运算符。这样做的结果是,开发者在某些场景下需要使用OQbject作为“值”时,必须使用QObject指针传递,而不能使用值传递。

qt编译mqqt中遇到的问题

环境:Qt5.11.1,mingw

1、注意,qt中的mqqt有几个版本,一个是官方网站上下载的mqqt,另一个是官方放在github上的mqqt,这两个稍微有点区别。最好下载官方网站上的,手动clone下来为准。

官方地址: https://codereview.qt-project.org/admin/repos/qt%2Fqtmqtt

2、qt官方的源码,需要安装perl,官方下载太慢,这里:https://www.jb51.net/softs/27286.html#downintro2

3、编译后,发现一大堆错误,一点点开始排错。

1)找不到文件。

在 Qt 的 include 目录下新建一个 QtMqtt 文件夹(注意 x86 和 x64 所在的目录不一样),把 头文件都复制一份到这个目录。

再编译就不报这个错误了。

2)qmqttconnection.cpp:169: error: C2039: “errorOccurred”: 不是“QAbstractSocket”的成员

改写成:

connect(socket, static_cast)(QAbstractSocket::SocketError)> (&QAbstractSocket::error), this, static_cast)(QAbstractSocket::SocketError)>(&QMqttConnection::transportError) );

3)resize不是QList<>的成员

改写成reserve

4)

  1. qmqtttopicfilter.cpp:245:69: error: ‘class QStringView’ has no member named ‘split’
  2. const QVector<QStringView> topicLevels = QStringView{topic}.split(QLatin1Char('/'));

改写成:topicLevels = topic.split(QLatin1Char(‘/’));还有一个d.filter也是一样的处理

5)qmqtttopicname.cpp:148: error: C2039: “KeepEmptyParts”: 不是“Qt”的成员

qmqtttopicname.cpp 148 行代码是这样的:

return d->name.split(QLatin1Char(‘/’), Qt::KeepEmptyParts);

确实不是Qt的成员,是QString的,因此, 改写成:return d->name.split(QLatin1Char(‘/’), QString::KeepEmptyParts);

然后就编译好了。。。。

再说在Qt中使用d指针的坑

在上篇转载文章(Qt中使用Q指针和D指针)中,介绍了一个宏来快速实现d指针和q指针。但是在最近使用中逐渐遇到了一些问题。

d指针和q指针本来是为了二进制兼容的,而二进制兼容最常用的地方就是各种库。在windows平台下,习惯定义各种接口,然后自行派生各种类,最后在动态库设置到处某一接口类。

在上述过程中,为了到处函数名称的一致性,通常将导出函数放在一个extern “C” {}中。如果你导出的是某个接口类,而还是按上文所演示的格式,在主类中私有类仅仅是一个前置声明的话,那么问题来了。上述宏中,使用了QScopedPointer,里面用到了sizeof这个运算符。

而sizeof,是不能用于extern的。于是,会报这样的错误:invalid application of ‘sizeof’ to incomplete type。

经过资料查找,sizeof是发生在编译时的,而extern修饰是在链接时解析,因此如果在主类中对私有类仅仅一个前置声明,在编译主类头文件,宏DQ_DECLARE_PRIVATE展开的时候,并不知道私有类的详细信息,因此报出上述错误。

于是,我们可以对症下药。针对上述的问题,将私有类的头文件写在主类头文件之前,而为了在私有类中使用宏DQ_DECLARE_PUBLIC,这里反而要将主类进行前置声明了。于是,就变成了下列的样子:

//Test.h

class Test;
class TestPrivate
{
    DQ_DECLARE_PUBLIC(Test)
public:
    TestPrivate(Test *q):q_ptr(q) {}
    Form_Test *form;
};
class Test : public IMode
{
    //...
}

Qt中使用Q指针和D指针

总结网上看到的文章,使用D指针的好处如下:

1.保证代码的二进制兼容性;

2.隐藏实现细节;

3.提高编译速度;

Qt关于D指针和Q指针的定义:

d_ptr指针指向私有实现类,使用如下宏定义辅助函数和声明友元类

#define Q_DECLARE_PRIVATE(Class) /  
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } /  
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } /  
    friend class Class##Private; 

 q_ptr指针指向父类,使用如下宏定义辅助函数和声明友元类

#define Q_DECLARE_PUBLIC(Class)                                    /  
    inline Class* q_func() { return static_cast<Class *>(q_ptr); } /  
    inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } /  
    friend class Class;  

 使用D指针和Q指针的宏定义


#define Q_D(Class) Class##Private * const d = d_func()  
#define Q_Q(Class) Class * const q = q_func()  
 

/*
   Some classes do not permit copies to be made of an object. These
   classes contains a private copy constructor and assignment
   operator to disable copying (the compiler gives an error message).
*/
#define Q_DISABLE_COPY(Class) \
    Class(const Class &) Q_DECL_EQ_DELETE;\

定义一个类如下:

class TestClassPrivate;
class TestClass
{
public:
    TestClass();
    ~TestClass();
 
private:
    TestClassPrivate * const d_ptr;
    Q_DECLARE_PRIVATE(TestClass);
};

定义一个私有类如下:

class TestClass;
class TestClassPrivate
{
public:
    TestClassPrivate(TestClass *q);
 
private:
    TestClass * const q_ptr;
    Q_DECLARE_PUBLIC(TestClass);
    
    int m_val1;
};

一个实例:

testclass.h

#ifndef TESTCLASS_H
#define TESTCLASS_H
 
#include <QObject>
 
class TestClassPrivate;
class TestClass
{
public:
    explicit TestClass(QObject *parent);
    ~TestClass();
    
    void doFunc();
    
private:
    void showMsg(QString str);
    
private:
    TestClassPrivate * const d_ptr;
    Q_DECLARE_PRIVATE(TestClass);
    Q_DISABLE_COPY(TestClass);
};
 
#endif // TESTCLASS_H

testclass.cpp


#include "testclass.h"
#include <QtDebug>
 
class TestClassPrivate
{
public:
    TestClassPrivate(TestClass *q) :
        q_ptr(q)
    {
    }
 
    void testFunc()
    {
        Q_Q(TestClass);
        q->showMsg("hello!");
    }
 
private:
    TestClass * const q_ptr;
    Q_DECLARE_PUBLIC(TestClass);
};
 
 
TestClass::TestClass(QObject *parent):
    d_ptr(new TestClassPrivate(this))
{
 
}
 
TestClass::~TestClass()
{
    Q_D(TestClass);
    delete d;
}
 
void TestClass::doFunc()
{
    Q_D(TestClass);
    d->testFunc();
}
 
void TestClass::showMsg(QString str)
{
    qDebug() << str;
}

以上是使用Qt自带宏实现。

 存在问题:

1.d_ptr需要手动释放,有可能粗心忘记了;

2.d_ptr和q_ptr的声明看起来不够简洁;

问题改进:

1.使用QScopedPointer,不用关注D指针的释放;

2.使用宏改进使用;

#define DQ_DECLARE_PRIVATE(Class) \
    Q_DECLARE_PRIVATE(Class) \
    QScopedPointer<Class##Private> d_ptr;
 
#define DQ_DECLARE_PUBLIC(Class) \
    Q_DECLARE_PUBLIC(Class) \
    Class* q_ptr;
 
#define DQ_SAFE_DELETE(p) do { if(p) { delete (p); (p) = 0; } } while(0)

使用实例:

dqglobal.h

#ifndef DQGLOBAL_H
#define DQGLOBAL_H
 
#include <QtGlobal>
#include <QScopedPointer>
 
#define DQ_DECLARE_PRIVATE(Class) \
    Q_DECLARE_PRIVATE(Class) \
    QScopedPointer<Class##Private> d_ptr;
 
#define DQ_DECLARE_PUBLIC(Class) \
    Q_DECLARE_PUBLIC(Class) \
    Class* q_ptr;
 
#define DQ_SAFE_DELETE(p) do { if(p) { delete (p); (p) = 0; } } while(0)
 
#endif // DQGLOBAL_H

testclass.h


#ifndef TESTCLASS_H
#define TESTCLASS_H
 
#include <QObject>
#include "dqglobal.h"
 
class TestClassPrivate;
class TestClass
{
    DQ_DECLARE_PRIVATE(TestClass)
public:
    explicit TestClass(QObject *parent);
    ~TestClass();
 
    void doFunc();
 
private:
    void showMsg(QString str);
 
//private:
//    TestClassPrivate * const d_ptr;
//    Q_DECLARE_PRIVATE(TestClass);
//    Q_DISABLE_COPY(TestClass);
};
 
#endif // TESTCLASS_H

testclass.cpp


#include "testclass.h"
#include <QtDebug>
 
class TestClassPrivate
{
    DQ_DECLARE_PUBLIC(TestClass)
public:
    TestClassPrivate(TestClass *q) :
        q_ptr(q)
    {
    }
 
    void testFunc()
    {
        Q_Q(TestClass);
        q->showMsg("hello!");
    }
 
//private:
//    TestClass * const q_ptr;
//    Q_DECLARE_PUBLIC(TestClass);
};
 
 
TestClass::TestClass(QObject *parent):
    d_ptr(new TestClassPrivate(this))
{
 
}
 
TestClass::~TestClass()
{
//    Q_D(TestClass);
//   delete d;
}
 
void TestClass::doFunc()
{
    Q_D(TestClass);
    d->testFunc();
}
 
void TestClass::showMsg(QString str)
{
    qDebug() << str;
}