【C&C++】C++类成员函数取地址以及类非静态成员函数和静态成员函数的区别

前言

相对于C语言,C++在类的设计中存在两点特殊设计:

  1. 成员函数重载
  2. 非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++ 函数指针 & 类成员函数指针
类成员函数作为回调函数的方法及注意点

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值