在c++里,当设计一个底层类的时候,下游不知道上游具体的类是什么,又需要调上游的方法(比如通过通信获取到了数据,需要将数据回传到上游),解决方法是将上游类的成员函数(其他函数同理)充当回调函数,传递给下游。

上游类:mainwindow

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class ServerProvider;

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
  Q_OBJECT

 public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow() override;

  void getData(const QString &str);

 private:
  Ui::MainWindow *ui;
  ServerProvider *m_serverProvider;
};
#endif  // MAINWINDOW_H


#include "mainwindow.h"

#include <functional>

#include "serverprovider.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);

  FuncDef func;
  func = std::bind(&MainWindow::getData, this, std::placeholders::_1);

  m_serverProvider = new ServerProvider();
  m_serverProvider->setFunc(func);
}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::getData(const QString &str) {
  qDebug() << "GET DATA: " << str;
}

下游类:serverprovider

#ifndef SERVERPROVIDER_H
#define SERVERPROVIDER_H

#include <functional>

class QString;

typedef std::function<void(const QString &str)> FuncDef;

class ServerProvider {
 public:
  ServerProvider();
  void setFunc(FuncDef func);

 private:
  FuncDef m_func;
};

#endif  // SERVERPROVIDER_H

#include "serverprovider.h"

#include <QString>

ServerProvider::ServerProvider() {}

void ServerProvider::setFunc(FuncDef func) {
  this->m_func = func;

  //假设此处获得数据
  //(实际场景这个方法只做setFunc,应该是在某个触发条件下调用m_func)
  QString str = "Hello World!";

  this->m_func(str);
}

如何使用QT自带的反射机制,运行槽函数呢?(QT6.2.3里信号和槽使用回调函数的原理就是这样,其他的还有兼容老式的用法)

//test.h
class Test {
 public:
  Test();
  ~Test();

  template <typename Func>
  void slotFlex(const typename QtPrivate::FunctionPointer<Func>::Object *sender,
                Func slot) {
    typedef QtPrivate::FunctionPointer<Func> SlotType;

    m_slotObject = new QtPrivate::QSlotObject<
        Func,
        typename QtPrivate::List_Left<typename SlotType::Arguments,
                                      SlotType::ArgumentCount>::Value,
        typename SlotType::ReturnType>(slot);

    m_object = const_cast<QObject *>(dynamic_cast<const QObject *>(sender));
  }

  void test();

 private:
  QtPrivate::QSlotObjectBase *m_slotObject;
  QObject *m_object;
};

//test.cpp
#include "test.h"

Test::Test() {}

Test::~Test() {
  if (m_slotObject != nullptr) {
    m_slotObject->destroyIfLastRef();
  }
}

void Test::test() {
  int a = 10;
  void *argv[] = {
      nullptr,
      const_cast<void *>(reinterpret_cast<const void *>(std::addressof(a))),
  };
  m_slotObject->call(m_object, argv);
}

使用:

//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class Test;
class MainWindow : public QMainWindow {
  Q_OBJECT

 public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();

 public slots:
  void testSlots(int a);

 private:
  Ui::MainWindow *ui;
  Test *m_test;
};
#endif  // MAINWINDOW_H

//mainwindow.cpp
#include "mainwindow.h"

#include "test.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);

  m_test = new Test();

  m_test->slotFlex(this, &MainWindow::testSlots);

  m_test->test();
}

MainWindow::~MainWindow() { delete ui; }

void MainWindow::testSlots(int a) { qDebug() << "slot flex" << a; }

原理就是使用typedef QtPrivate::FunctionPointer<Func>获取函数信息,包括参数,返回值等,生成一个slotObject对象,然后调用call方法。

QSlotObject是 QtPrivate::QSlotObjectBase的子对象,将impl方法传递给父类,QtPrivate::QSlotObjectBase调用impl方法

static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
        {
            switch (which) {
            case Destroy:
                delete static_cast<QSlotObject*>(this_);
                break;
            case Call:
                FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
                break;
            case Compare:
                *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
                break;
            case NumOperations: ;
            }
        }

点墨
26 声望3 粉丝

全栈前端开发工程师