我是靠谱客的博主 听话山水,最近开发中收集的这篇文章主要介绍关于C++中的非静态类成员函数指针,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

  昨天发现了一个问题,就是使用对类中的非静态成员函数使用std::bind时,不能像普通函数一样直接传递函数名,而是必须显式地调用&(取地址),于是引申出我们今天的问题:非静态类成员函数指针和普通函数指针有什么区别?

一.C++中对函数到指针的隐式转换

  以前在C语言程序设计课上,老师都会说:“函数名就是指向这个函数的指针”。实际上通过查阅cppreference中的隐式转换规则,其中有这么一句关键的话道出了玄机:

函数类型 T 的左值能隐式转换成指向该函数的指针纯右值。这不作用于非静态成员函数,因为不存在指代非静态成员函数的左值。

  这一句话表明,实际上我们在把普通函数的函数名当作右值使用的过程中,C++隐式地将其转换成了指向该函数的的指针。这也就是我们在使用std::bind的过程中可以直接传递普通函数的函数名的原因,例如:

int foo(int a,int b)
{
    return (a+b);
}

int main()
{
    auto function = std::bind(foo,std::placeholders::_1,std::placeholders::_2);
    std::cout << function(3,4) << std::endl; //7
    return 0;
}

  但是,这一条隐式转换规则不作用于非静态成员函数,因为不存在指代非静态成员函数的左值。因此我们能看出,指向非静态成员函数的指针和指向普通函数的指针是有区别的。接下来我们将会讨论类成员函数指针(除非有特别说明,下文中的“类成员”都指代“非静态类成员”)。

 

二.类成员函数指针的声明和调用

  类成员函数指针的声明有些类似普通函数指针。但是区别在于,类成员函数是类成员的一部分,所以需要将类成员函数指针使用 ::* 声明成 成员指针类型。这也表示了类成员函数指针不能单独地被调用,需要 该类型的实例使用.*(对象的成员指针)指向该类型的指针使用->*(指针的成员指针) 进行调用。同时由于上文说到的C++隐式转换的规则,我们需要显示地调用 &(取地址) 获取非静态成员函数指针:

class A
{
public:
    void FunA()
    {
        std::cout << "Function A" << endl;
    }
};

int main()
{
    void(A::*p1)() = &A::FunA;
    //p1(); Error
    A a;
    A *p2 = &a;
    (a.*p1)(); // Succ
    (p2->*p1)(); // Succ
    return 0;
}

 

三.探究类成员函数指针

  我们都知道,普通函数指针实际上指向的是函数代码的起始地址。然而类成员函数指针不仅仅是类成员函数的内存起始地址,它还解决C++中涉及到的多重继承、虚函数等导致的地址偏移问题。因此,普通函数指针的内存大小是普通指针的大小(32位系统4字节,64位系统8字节),但是成员函数指针却不一定。

1.多继承中的使用

  现在有三个类A B C,其中类C public继承 了A B两个类。它们的声明和定义如下:

class A
{
public:
  int a;
  void funA()
  {
    std::cout << "function A" << std::endl;
  }
};

class B
{
public:
  int b;
  void funB()
  {
    std::cout << "function B" << std::endl;
  }
};

class C : public A, public B
{
public:
  int c;
  void funC()
  {
    std::cout << "function C" << std::endl;
  }
};

  现在我们声明一个函数指针:

int main()
{
  void (C::*mfpC)() = &B::funB;
  return 0;
}

  这个指针很特别,它是一个指向C的类成员的指针,但实际上它指向了C的基类B的成员函数B::funB。现在我们编译代码并调用gdb调试查看mfpC的信息:

(gdb) p mfpC
$1 = (void (C::*)(C * const)) 0x8000966 <B::funB()>, this adjustment 4
(gdb) p sizeof(mfpC)
$2 = 16
(gdb) x/16xb &mfpC
0x7ffffffee830: 0x66    0x09    0x00    0x08    0x00    0x00    0x00    0x00
0x7ffffffee838: 0x04    0x00    0x00    0x00    0x00    0x00    0x00    0x00

  可以看见,mfpC实际占用了16个字节(运行环境为64位操作系统),这16个字节中不仅包含了它所指向的函数的起始地址,还包含了this指针的调整值。因为对于多重继承来说,如果类成员函数指针保存的是非最左基类的成员函数地址,根据C++标准,非最左基类实例的开始地址肯定不同于派生类实例的开始地址,所以需要调整this指针,使其指向非最左基类实例。

2.有虚函数的使用

  现在修改代码,在类B中定义一个虚函数funC,并在类C中重写它:

class A
{
public:
  int a;
  void funA()
  {
    std::cout << "function A" << std::endl;
  }
};

class B
{
public:
  int b;
  void funB()
  {
    std::cout << "function B" << std::endl;
  }

  virtual void funC()
  {
    std::cout << "function C in class B" << std::endl;
  }
};

class C : public A, public B
{
public:
  int c;
  void funC()
  {
    std::cout << "function C in class C" << std::endl;
  }
};

  然后我们实例化一个类C的对象c和一个指向B::funC的成员函数指针mfpB,最后通过c调用它:

int main()
{
  C c;
  void (B::*mfpB)() = &B::funC;
  (c.*mfpB)();
  return 0;
}

  结果显而易见,输出的不是"function C in class B",而是"function C in class C"。

  由此可见,成员函数指针虽然看起来和普通的函数指针有些类似,但实际上两者还是有很大的差别的。

转载于:https://www.cnblogs.com/RodChong/p/9890447.html

最后

以上就是听话山水为你收集整理的关于C++中的非静态类成员函数指针的全部内容,希望文章能够帮你解决关于C++中的非静态类成员函数指针所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部