前言
相对于C语言,C++在类的设计中存在两点特殊设计:
- 成员函数重载
- 非static的成员函数,编译器在编译该成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this.
而本文中,与第二点特性相关.
在类中,static不仅可以进行静态成员变量的声明,还可以进行静态成员函数的声明。普通成员函数可以访问所以成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。
普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。
普通成员变量占用对象的内存,静态成员函数没有 this 指针,不知道指向哪个对象,无法访问对象的成员变量,也就是说静态成员函数不能访问普通成员变量,只能访问静态成员变量。
普通成员函数必须通过对象才能调用,而静态成员函数没有 this 指针,无法在函数体内部访问某个对象,所以不能调用普通成员函数,只能调用静态成员函数。
重点: 静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
而本文讲解的是将C++类的成员函数作为回调函数进行注册, 为什么该成员函数需要时静态成员函数?
实例讲解
以下基于一个hello类的实现,来演示C++中类的成员作为callback的实现方式,
Hello.h的实现,
#include<iostream>
namespace hi{
class hello{
public:
typedef void (*eventCallback)(void);
hello();
~hello();
void say_hi();
void setEventCallback(eventCallback c);
private:
eventCallback cb;
void onEvent();
};
}
Hello.cpp的实现,
#include "Hello.h"
namespace hi{
hello::hello()
{
std::cout << "construct hello" << std::endl;
setEventCallback(onEvent);
}
hello::~hello()
{
std::cout << "destruct hello" << std::endl;
}
void hello::say_hi()
{
std::cout << "say hi" << std::endl;
if( cb != NULL )
{
cb();
}
}
void hello::setEventCallback(eventCallback c){
cb = c;
}
void hello::onEvent()
{
std::cout << "onEvent say hi" << std::endl;
}
}
main.cpp 的实现如下,
#include <iostream>
#include "Hello.h"
int main(int argc, char **qrgv){
hi::hello *hi = new hi::hello;
hi->say_hi();
delete hi;
return 0;
}
以上实现的代码非常简单,在这里就没有进行解释了.
然后在Linux的终端下,编译以上代码,
➜ C++ g++ Hello.cpp Hello.h main.cpp
Hello.cpp: In constructor ‘hi::hello::hello()’:
Hello.cpp:8:29: error: invalid use of non-static member function ‘void hi::hello::onEvent()’
setEventCallback(onEvent);
^
Hello.h:17:10: note: declared here
void onEvent();
^~~~~~~
然而结果是编译报错了,从编译的错误信息来看是因为onEvent 是非静态成员函数.
Hello.cpp:8:29: error: invalid use of non-static member function ‘void hi::hello::onEvent()’
此刻你可能会产生疑问?hello类的成员函数setEventCallback的函数指针类型是
void (*eventCallback)(void)
而void hello::onEvent()是匹配的,这里为什么会报错呢?
而这里的根因在于本文章***前言***中所描述到的,hello类中定义实现的onEvent是非静态的成员函数,那么编译器在编译的时候会隐式地增加一个形参 this,并把当前对象的地址赋值给 this. 这就会导致与setEventCallback中所定义的函数指针类型不符合,多了一个参数this.
那么?怎么修正才能作为回调函数进行注册呢?
其实编译器已经给出了明确的提示: error: invalid use of non-static member
所以我对,Hello.h 做了简单修改,将onEvent增加static修饰, onEvent修正为静态成员函数.
#include<iostream>
namespace hi{
class hello{
public:
typedef void (*eventCallback)(void);
hello();
~hello();
void say_hi();
void setEventCallback(eventCallback c);
private:
eventCallback cb;
static void onEvent();
};
}
然后编译运行,运行结果如下:
➜ C++ ./a.out
construct hello
say hi
onEvent say hi
destruct hello
如上所示,在通过hello类的实例化对象执行成员函数say_hi时,onEvent被正确回调了.
总结
回调函数的使用,在我们的日常开发中常用的方式,希望通过本篇文章能够更好的理解C++中对于回调函数声明定义时的细节点,另外也能通过这样一个简单的实例进一步加深对C++中通过static进行成员函数的修饰.
参考链接:
GLib 中实现C++成员函数的回调注册
C++ static静态成员函数详解
C++ 函数指针 & 类成员函数指针
类成员函数作为回调函数的方法及注意点