[C++] 使用指针调用类成员方法

关键词: 类成员方法 函数指针 类间函数调用  std::function


目录

结论

摘要

0 引子

1 使用静态成员方法

2 使用this-> 传递函数指针

3 函数包裹

3.1 C语言形式的包裹(指针包裹)

3.2 C++ 形式的包裹

3.3 std::funtion 包裹(优雅的实现)

4 拓展

4.1 std::funtion 详解

4.2 成员函数指针长度

5 应用

5.1 多线程创建

5.2 接收与处理分离


结论

任何指向“类”的成员函数指针,由于携带额外的所属对象信息,与一般的函数有根本的不同,不能直接用来进行函数调用。用C++ 描述:对于类class MyClass 中的成员函数 void Method(); ,它的函数类型为void (MyClass::*f)(); 而不是void (*f)(); 。

摘要

对于C语言来说,函数指针是可以任意传递的。这跟语言特性有关系,C语言是一种静态的,在程序运行前分配函数地址的。而C++是动态的,在对象申请时,才会有成员函数的地址。因此,对于C++使用者而言,一个类中方法是不能通过直接传递的方式获取另外一个类的成员函数指针的。你在传递成员函数的地址时,还要传递该对象的指针。

0 引子

在C语言中,你可以转换任意函数的指针,通过传递函数指针的方式将目标函数传递给目标调用。函数指针类型是全局的,任意传递的。它是一种不够安全的方法。比如:

#include <iostream>

void Add(int a, int b)
{
    std::cout << a+b;
}

int main()
{
    auto f = Add;
    f(1, 2);  
    return 0;
}

在C++ 中,方法通常被类包裹。类成员方法包含类名称。比如:

class MyClass
{
public:
    MyClass();
    ~MyClass();
    void MyClassMethod();
private:
};

如果你需要使用成员函数指针,那么你可以这样做。

1 使用静态成员方法

将类的成员方法声明为静态的,该方法归属于类而非对象。在程序运行前分配地址,因此,这是一种更像C语言的使用方法。通过

&类::成员方法

可以获取到函数指针。

MyClass::MyClass(){}
MyClass::~MyClass(){}

void MyClass::MyClassMethod(){ std::cout << "Hello"; }

int main()
{
    auto f = &MyClass::MyClassMethod;
    f();
    return 0;
}

注意:静态成员方法调用的方法必须是静态的。

2 使用this-> 传递函数指针

如果指针在同一个类中传递,比如:

#include <iostream>

class MyClass
{
public:
    MyClass();
    ~MyClass();
    void MyClassMethod();
private:
    void InvokeMethod();
};

MyClass::MyClass(){}

MyClass::~MyClass(){}

void MyClass::MyClassMethod()
{
    auto  f = &MyClass::InvokeMethod;
    (this->*f)();
}

void MyClass::InvokeMethod()
{
    std::cout << "Hello";
}

int main()
{
    MyClass my_class;
    my_class.MyClassMethod();
    return 0;
}

3 函数包裹

一个类调用另一个类的函数指针的时候可以采用函数包裹的方式,具体有如下三种

3.1 C语言形式的包裹(指针包裹)

本质上是将多级指针转换为1级指针。对于一个对象的成员函数的方法,通过将二级指针调用,封装成C语言函数调用。

#include <iostream>

typedef void(*f)();

void function(InvokeClass* myIncvokeClass)
{
    myIncvokeClass->InvokeMethod();
}

class MyClass
{
public:
    MyClass();
    ~MyClass();
    void TestMethod(f myFunction);
private:
};

MyClass::MyClass(){}

MyClass::~MyClass(){}

void MyClass::TestMethod(f myFunction)
{
    myFunction();
}

class InvokeClass
{
public:
    InvokeClass();
    ~InvokeClass();
    void InvokeMethod();
private:
};

InvokeClass::InvokeClass(){}

InvokeClass::~InvokeClass(){}

void InvokeClass::InvokeMethod()
{
    std::cout << "hello";
}

int main()
{
    auto myIncvokeClass = new InvokeClass();
    auto f = function;
    f(myIncvokeClass);
    return 0;
}

3.2 C++ 形式的包裹

#include <iostream>

class InvokeClass
{
public:
InvokeClass();
~InvokeClass();
void InvokeMethod();
private:
};

InvokeClass::InvokeClass(){}

InvokeClass::~InvokeClass(){}

void InvokeClass::InvokeMethod()
{
std::cout << "hello";
}

class MyClass
{
public:
MyClass();
~MyClass();
void TestMethod(InvokeClass* myInvokeClass);
private:
};

MyClass::MyClass(){}

MyClass::~MyClass(){}

void MyClass::TestMethod(InvokeClass* myInvokeClass)
{
myInvokeClass->InvokeMethod();
}

int main()
{
MyClass myClass;
auto  myInvokeClass = new InvokeClass;
myClass.TestMethod(myInvokeClass);
}

因2中的声明为 void TestMethod(InvokeClass* myInvokeClass); 只能传递一个类对象指针

可将2中声明为

template <typename T>
void TestMethod(T* myInvokeClass);

或者

void TestMethod(InvokeClass1* myInvokeClass);
void TestMethod(InvokeClass2* myInvokeClass);
void TestMethod(InvokeClass3* myInvokeClass);

3.3 std::funtion 包裹(优雅的实现)

#include <iostream>
#include <functional>

class MyClass
{
public:
    MyClass();
    ~MyClass();
public:
    void Method(std::function<int(int, int)> f);
};

MyClass::MyClass(){};
MyClass::~MyClass(){};

void MyClass::Method(std::function<int(int, int)> f)
{
    std::cout << f(1, 2);
}

class InvokeClass
{
public:
    InvokeClass();
    ~InvokeClass();
    int Add(int i, int j) {
        return i + j;
    }
private:
};

InvokeClass::InvokeClass(){}

InvokeClass::~InvokeClass(){}

int main()
{
    InvokeClass myInvokeClass;
    MyClass myClass;
    std::function<int(int, int)> f = bind(&InvokeClass::Add,\\
                        &myInvokeClass,std::placeholders::_1, std::placeholders::_2);   
    myClass.Method(f);
    return 1;
}

4 拓展

4.1 std::funtion 详解

std::function详解_在座的各位都是高手的博客-CSDN博客_std::functionstd::function详解https://blog.csdn.net/weixin_44378800/article/details/115210731

        std::funtion 函数包装器模板,函数包裹器,将可调用对象包裹后只保留特征。

4.2 成员函数指针长度

注意:在传递成员函数的地址时,要传递该对象的指针。它是一个二级指针,隐式的转换可能存在风险,在更高级的 C++ 的编译器中,二级指针是不允许被隐式的转换的,如果你要使用它,请使用显示的表达法。在下面的程序中,写一个简单的测试函数查看各种类的成员函数指针和普通函数指针的长度并输出到屏幕上。

#include <iostream>
class MyClass;  
class MyClass2{};
class MyClass3   //一个有定义的类。
{
public:
    void (* memberfun)();
    void Memberfun1( void (* f2)( ) ) { f2( ) ;} //成员函数1调用成员函数//2。
    void Memberfun2( );//成员函数2。
};

class MyClass4: virtual MyClass3 ,MyClass2  //一个有virtual继承的类(derivative class)。
{
public:
               void Memberfun1( void (* f2)( ) ) { f2( ) ;}
};

class MyClass5: MyClass3,MyClass2  //一个继承类(derivative class)。
{
               public:
               void Memberfun1( void (* f2)( ) ) { f2( ) ;}
};

int main()
{
    std::cout << "一般函数指针长度= " << sizeof(void(*)()) << '\n';
    std::cout <<"-类的成员函数指针长度-"<<'\n'<<'\n';
    std::cout <<"MyClass3类成员函数指针长度="<< sizeof(void(MyClass3::*)())<<'\n'<<'\n';
    std::cout <<"MyClass5类成员函数指针长度="<<sizeof(void (MyClass5:: *)())<<'\n';
    std::cout <<"MyClass4类成员函数指针长度="<<sizeof(void (MyClass4:: *)())<<'\n';
    std::cout <<"MyClass类成员函数指针长度="<<sizeof(void(MyClass::*)()) <<'\n';
    return 0;     
  
}

输出结果:

一般非成员函数指针长度= 4

类的成员函数指针长度:

MyClass3类成员函数指针长度=4

MyClass5类成员函数指针长度=8

MyClass4类成员函数指针长度=12

MyClass类成员函数指针长度=16

以上结果表明,一般函数指针的长度为4个字节,而类的成员函数指针的长度随类的定义与否、类的继承种类和关系而变,从无继承关系类(MyClass3)的4字节(32位)到有虚继承关系类(Virtual Inheritance)(MyClass4)的12字节(96位),仅有说明(declaration)没有定义的类(MyClass)因为与其有关的一些信息不明确成员函数指针最长为16字节(128位)。显然,与一般函数指针不同,指向“类”的成员函数的指针不仅包含成员函数地址的信息,而且包含与类的属性有关的信息。因此,一般函数指针和类的成员函数指针是根本不同的两种类型。当然,也就不能用一般函数指针直接调用类的成员函数。尽管使用较早版本的编译软件编译仍然可以通过,但这会给程序留下严重的隐患。

5 应用

5.1 多线程创建

向 std::thread 传递成员函数指针。同时需要传递该对象的指针。

#include <iostream>
#include <thread>

class MyClass
{
public:
    MyClass();
    ~MyClass();
    void TestMethod();
private:
};

MyClass::MyClass(){}

MyClass::~MyClass(){}

void MyClass::TestMethod()
{
    std::cout << "hello";
}

int main()
{
    auto myClass = new MyClass();
    auto myThread = std::thread(&MyClass::TestMethod,myClass);
    return 0;
}

5.2 接收与处理分离


#include <iostream>
#include <thread>

//TODO
class Rcv
{
public:
Rcv();
~Rcv();
private:
};

Rcv::Rcv(){}

Rcv::~Rcv(){}

//TODO
class Handle
{
public:
Handle();
~Handle();
private:
void Handle1();
};

Handle::Handle(){}

Handle::~Handle(){}

int main(){
    //TODO

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值