回调函数和this指针

一 回调函数

前言

在Qt中传数据,我们使用了一个信号和槽系统,但这并不意味着不能使用旧的经过验证的方法,即使用 CallBack回调函数功能。
事实上使用 CallBack 功能比信号和槽要快一些。并且当发送信号的对象在程序中被销毁并且不再使用时,就信号理想地从槽中分离而言,回调可以更容易使用。


如何使用CallBack工作

假设A类包含B类的对象,B类有动作时想要通知到A类,B类应该有个设置回调函数的接口,A类应该定义相应的回调函数,将函数指针传递给B。

还是直接举例吧:
例如,将使用一个类,在图形场景中绘制一个正方形,并由W,A,S,D键控制。移动时,正方形必须将其坐标的数据发送到创建它的类。也就是说,它应该把这个类的函数作为它的 CallBack 函数。要做的程序效果如下,通过WASD控制方块移动,主窗口接收正方形的位置信息,并将位置信息填入QLineEdit:

square.h中部分代码:

class Square : public QObject, public QGraphicsItem
{
    Q_OBJECT

public:
    explicit Square(QObject *parent = 0);
    ~Square();

    // 设置回调函数的函数
    void setCallbackFunc(void(*func) (QPointF point));

protected:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private:
    QTimer * m_timer;

    void(*m_callbackFunc)(QPointF point);

private slots:
    void slotTimer();
};

square.cpp中部分代码:

Square::Square(QObject *parent)
    : QObject(parent), QGraphicsItem()
{
    m_timer = new QTimer();
    connect(m_timer, &QTimer::timeout, this, &Square::slotTimer);
    m_timer->start(1000 / 33);
}

Square::~Square()
{
}

void Square::setCallbackFunc(void(*func) (QPointF point))
{
    m_callbackFunc = func;
}

QRectF Square::boundingRect() const
{
    return QRectF(-15, -15, 30, 30);
}

void Square::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::black);
    painter->setBrush(Qt::green);
    painter->drawRect(-15, -15, 30, 30);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void Square::slotTimer()
{
    // 根据按钮触发情况移动正方形
    if (GetAsyncKeyState('A')) 
    {
        this->setX(this->x() - 2);
    }
    if (GetAsyncKeyState('D')) 
    {
        this->setX(this->x() + 2);
    }
    if (GetAsyncKeyState('W')) 
    {
        this->setY(this->y() - 2);
    }
    if (GetAsyncKeyState('S')) 
    {
        this->setY(this->y() + 2);
    }
    // 调用回调函数传递正方形位置  类似于发信号给mainwindow  由mainwindow执行相应槽函数
    m_callbackFunc(this->pos());
}

mainwindow.h部分代码

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = Q_NULLPTR);
    ~MainWindow();

private:
    Ui::MainWindow* ui;

    QGraphicsScene* m_scene;
    Square *m_square;           // 声明正方形 传输回调
    static QLineEdit *line1;    // 声明一个静态QLineEdit, 执行回调
    static QLineEdit *line2;    // 声明一个静态QLineEdit, 执行回调

private:
    // 声明一个回调函数
    static void getPosition(QPointF point);
};

mainwindow.cpp部分代码

QLineEdit * MainWindow::line1;
QLineEdit * MainWindow::line2;

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

    // 初始化QLineEdit
    line1 = new QLineEdit();
    line2 = new QLineEdit();

    // 把两个line 放进gridLayout
    ui->gridLayout->addWidget(line1, 0, 1);
    ui->gridLayout->addWidget(line2, 0, 2);

    // 初始化图形场景
    m_scene = new QGraphicsScene();       
    // 设置场景到 graphicsView
    ui->graphicsView->setScene(m_scene);  
    m_scene->setSceneRect(0, 0, 300, 300);
    m_square = new Square();
    // 将getPosition设置回调  接收m_square传入的数据
    m_square->setCallbackFunc(getPosition);       
    m_square->setPos(100, 100);
    m_scene->addItem(m_square);
}

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

/** 
 * @fn     MainWindow::getPosition
 * @brief  回调函数接收正方形位置 写入两个lineEdit
 * @param  QPointF point
 * @return void
 */
void MainWindow::getPosition(QPointF point)
{
    line1->setText(QString::number(point.x()));
    line2->setText(QString::number(point.y()));
}

类内成员函数作为回调函数为什么需要加static?
  在类中封装回调函数,回调函数必须要加上static关键字,这样this指针就不能用了,类中静态函数只能访问类的静态成员,不能访问类中的非静态成员,静态函数属于这个类,而不再仅仅属于具体的对象,类的静态函数中没有默认的this指针。但是为什么要这要做呢?

//1.例如一个回调函数被要求声明为以下形式(不属于具体的对象):
void CALLBACK function();

//2.如果这个函数在类ObjClass里面,编译器会为其添加一个this指针,
//用于指向调用该函数的对象。所以编译出来的代码是这种形式:

void CALLBACK ObjClass::function(ObjClass* this)

函数参数列表与被要求声明的形式不一致。就比如说,主窗口中回调函数中有this指针,但是子部件调用的时候可没有,导致参数不匹配。因此我们应该废弃this,而我们也恰巧需要使用主窗口类中的控件,我们可以把对象实例的指针或引用做为参数,和回调函数地址一并传给子部件。

备注:回调函数也可以声明在类外,全局的传类对象指针给回调函数即可使用类内成员,但是回调函数是全局的,所以会影响类的封装性

二 this指针

        一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。

使用示例

        this指针是类的一个自动生成、自动隐藏的私有成员,它存在于类的非静态成员函数中,指向被调用函数所在的对象。全局仅有一个this指针,当一个对象被创建时,this指针就存放指向对象数据的首地址。根据以下程序来说明this指针?

#include<iostream>
using namespace std;
class Point
{
  private:
  int x,y;
  public:
  Point(int a,int b)
  {
    x=a;
    y=b;
  }
  void MovePoint(int a,int b)
  {
    x+=a;
    y+=b;
  }
  void print()
  {
    cout<<"x="<<x<<"y="<<y<<endl;
  }  
};
int main()
{
  Point point1(10,10);
  point1.MovePoint(2,2);
  point1.print();
  return 0;
}

        当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。MovePoint函数的原型应该是 void MovePoint( Point *this, int a, int b);

第一个参数是指向该类对象的一个指针,我们在定义成员函数时没看见是因为这个参数在类中是隐含的。这样point1的地址传递给了this,所以在MovePoint函数中便显式的写成:void MovePoint(int a, int b) { this->x +=a; this-> y+= b;}

即可以知道,point1调用该函数后,也就是point1的数据成员被调用并更新了值。

应用参考

        在前面曾经提到过: 每个对象中的数据成员都分别占有存储空间,如果对同一个类定义了n个对象,则有n组同样大小的空间以存放n个对象中的数据成员。

        但是,不同对象都调用同一个函数代码段。那么,当不同对象的成员函数引用数据成员时,怎么能保证引用的是所指定的对象的数据成员呢?

        假如,对于例9.6程序中定义的Box类,定义了3个同类对象a,b,c。如果有a.volume( ) ,应该是引用对象a中的height,width和length,计算出长方体a的体积。

        如果有b.volume( ) ,应该是引用对象b中的height,width和length,计算出长方体b的体积。而现今都用同一个函数段,系统怎样使它分别引用a或b中的数据成员呢?在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为this指针。

        它是指向本类对象的指针,它的值是当前被调用的成员函数所在的对象的起始地址。例如,当调用成员函数a.volume时,编译系统就把对象a的起始地址赋给this指针,于是在成员函数引用数据成员时,就按照this的指向找到对象a的数据成员。

        例如volume函数要计算height*width*length的值,实际上是执行:(this->height)*(this->width)*(this->length)由于当前this指向a,因此相当于执行:(a.height)*(a.width)*( a.length)这就计算出长方体a的体积。同样如果有b.volume( ) ,编译系统就把对象b的起始地址赋给成员函数volume的this指针,显然计算出来的是长方体b的体积。

        this指针是隐式使用的,它是作为参数被传递给成员函数的。本来,成员函数volume的定义如下:int Box::volume( ){return (height*width*length);}C++把它处理为int Box::volume(Box *this){return (this->height * this->width * this->length);}即在成员函数的形参表列中增加一个this指针。在调用该成员函数时,实际上是用以下方式调用的:a.volume(&a);将对象a的地址传给形参this指针。然后按this的指向去引用其他成员。

        需要说明: 这些都是编译系统自动实现的,编程序者不必人为地在形参中增加this指针,也不必将对象a的地址传给this指针。在需要时也可以显式地使用this指针。

        例如在Box类的volume函数中,下面两种表示方法都是合法的、相互等价的。return (height * width * length); //隐含使用this指针return (this->height * this->width * this->length); //显式使用this指针可以用*this表示被调用的成员函数所在的对象,*this就是this所指向的对象,即当前的对象。例如在成员函数a.volume( )的函数体中,如果出现*this,它就是本对象a。

        上面的return语句也可写成return((*this).height * (*this).width * (*this).length);注意*this两侧的括号不能省略,不能写成*this.height。所谓“调用对象a的成员函数f”,实际上是在调用成员函数f时使this指针指向对象a,从而访问对象a的成员。在使用“调用对象a的成员函数f”时,应当对它的含义有正确的理解。

原文地址:

在Qt中使用回调函数替代信号槽_qt信号槽和回调函数_lesliefish的博客-CSDN博客

QT 回调函数来龙去脉_qt回调函数例子_daboluo520的博客-CSDN博客

C++this指针_百度百科

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值