专栏首页C/C++基础C++类成员指针

C++类成员指针

1.成员指针简介

成员指针是C++引入的一种新机制,它的申明方式和使用方式都与一般的指针有所不同。成员指针分为成员函数指针和成员数据指针。

2. 成员函数指针

在事件驱动和多线程应用中被广泛用于调用回调函数。在多线程应用中,每个线程都通过指向成员函数的指针来调用该函数。在这样的应用中,如果不用成员指针,编程是非常困难的。

成员函数指针的定义格式: 成员函数返回类型 (类名::*指针名)(形参)= &类名::成员函数名

成员指针使用示例:

#include <iostream>
#include <string>
using namespace std;

class A{
    string name;

public:
    A(string s){
        name=s;
    }

    void print(){
        cout<<"name:"<<name<<endl;
    }
};

int main()
{
    A a("lvlv");
    void (A::*memP)()=&A::print; //定义类成员函数指针并赋初值
    (a.*memP)();
}

程序正常运行并输出: name:lvlv

使用成员函数指着注意两点: (1)使用成员函数指针时需要指明成员函数所属的类对象,因为通过指向成员函数的指针调用该函数时,需要将对象的地址用作this指针的值,以便进行函数调用;

(2)为成员函数指针赋值时,需要显示使用&运算符,不能直接将类名::成员函数名赋给成员函数指针。

3. 成员数据指针

一个类对象生成后,它的某个成员变量的地址实际上由两个因素决定:对象的首地址和该成员变量在对象之内的偏移量。成员数据指针是用来保存类的某个成员数据在类对象内的偏移量的。它只能用于类的非静态成员变量。

成员数据指针的定义格式: 成员数据指针的定义格式:成员类型 类名::*指针名=&类名::成员名;

成员数据指针使用示例:

#include <iostream>
using namespace std;

class Student{
public:
    int age;
    int score;
};

double average(Student* objs,int Student::*pm,int count){
    int result=0;
    for(int i=0;i<count;++i)
        result+=objs[i].*pm;
    return double(result)/count;
}

int main()
{
    Student my[3]={{16,86},{17,80},{18,58}};
    double ageAver=average(my,&Student::age,3);//求平均年龄
    double scoreAver=average(my,&Student::score,3);//求平均成绩
    cout<<"ageAver:"<<ageAver<<endl;
    cout<<"scoreAver:"<<scoreAver<<endl;
}

程序输出如下结果: ageAver:17 scoreAver:74.6667

使用成员数据指针时,需要注意以下几点: (1)成员数据指针作为一个变量,在底层实现上,存放的是对象的数据成员相对于对象首地址的偏移量,因此通过成员数据指针访问成员变量时需要提供对象的首地址,即通过对象来访问。从这个意义上说,成员数据指针并不是一个真正的指针。

(2)对象的成员数据指针可以通过常规指针来模拟,例如上面的程序中,可以讲average()函数的形参pm可以申明为int型变量,表示数据成员的偏移量,那么原来的obj.*pm等同于*(int*)((char*)(&obj)+pm),显然,这样书写可读性差,可移植性低且容易出错。

(3)使用成员数据指针时,被访问的成员往往是类的公有成员,如果是类的私有成员,容易出错。考察如下程序。

#include <iostream>
using namespace std;
class ArrayClass{
    int arr[5];
public:
    ArrayClass(){
        for(int i=0;i<5;++i)
            arr[i]=i;
    }
};

//使用成员数据指针作为形参
void printArray(ArrayClass& arrObj,int (ArrayClass::* pm)[5]){
    for(int i=0;i<5;++i)
        cout<<(arrObj.*pm)[i]<<" ";
}

int main()
{
    ArrayClass arrObj;
    printArray(arrObj,&ArrayClass::arr);//编译出错,提示成员ArrayClass::arr不可访问
}

以上程序无法通过编译,因为成员arr在类ArrayClass中的访问权限设置为private,无法访问。要解决这个问题,将函数printArray()设置为类ArrayClass的友元函数是不行的,因为是在调用该函数是访问了类ArrayClass的私有成员,而不是在函数体内用到类ArrayClass的私有成员。因此,可以定义一个调用printArray()函数的友元函数。该函数的参数中并不需要传递类ArrayClass的私有成员。修改后的程序如下。

#include <iostream>
using namespace std;

class ArrayClass{
int arr[5];
public:
    ArrayClass(){
        for(int i=0;i<5;++i)
            arr[i]=i;
    }

    friend void print(ArrayClass& arrObj);
};

//使用成员数据指针作为形参
void printArray(ArrayClass& arrObj,int (ArrayClass::* pm)[5]){
    for(int i=0;i<5;++i)
        cout<<(arrObj.*pm)[i]<<" ";
}

//定义友元函数
void print(ArrayClass& arrObj){
    printArray(arrObj,&ArrayClass::arr);
}

int main()
{
    ArrayClass arrObj;
    //printArray(arrObj,&ArrayClass::arr);//编译出错,提示成员ArrayClass::arr不可访问
    print(arrObj); //通过友元函数调用打印数组函数printArray()来访问私有成员
    getchar();
}

程序通过编译,运行输出0,1,2,3,4。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Shell函数

    函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高。像其他编程语言一样,Shell也支持函数。Shell函数必须先定义后使用。

    Dabelv
  • 函数申明对函数模板实例化的屏蔽

    C++语言引入模板机制后,函数调用的情形显的比C语言要复杂。当发生一次函数调用时,如果存在多个同名函数,则C++编译器将按照如下的顺序寻找对应的函数定义。 ...

    Dabelv
  • 野指针

    指向非法的内存地址指针叫作野指针(Wild Pointer),也叫悬挂指针(Dangling Pointer),意为无法正常使用的指针。

    Dabelv
  • 走进Java接口测试之解决超大文本数据驱动报OOM问题

    上篇文章 走进Java接口测试之测试框架TestNG数据驱动(入门篇)阐述测试框架 TestNG 中的一些基本的概念和玩法,本文带着大家来解决一个实际的工程问题...

    高楼Zee
  • 如何在Python中从0到1构建自己的神经网络

    大多数关于神经网络的介绍性文章在描述它们时都会提到大脑类比。在不深入研究大脑类比的情况下,我发现简单地将神经网络描述为将给定的输入映射到期望的输出的数学函数就更...

    liuxuewen
  • 编程基础知识:函数签名学习

    编程功能的基本单位。你建立你的程序一个函数(或方法)。最小的东西您可以测试在单元测试是一个函数。也是最小的一个函数的代码名称,因此可以创建一个新的抽象。函数的目...

    用户1289394
  • EEPROM和flash这样讲,我早就懂了

    https://blog.csdn.net/yuanlulu/article/details/6163106

    MCU起航
  • Scala 最佳实践:纯函数

    我们所处的是一个命令式编程(imperative programming)的时代,这也是我们为何更喜欢用命令式风格写代码的原因。在我们周围的一切都是可变的。虽然...

    用户1558438
  • C++基础——函数

      返回类型是必须的,当没有返回类型的时候就用void代替,如果参数个数超过1,则用逗号分隔参数列表,参数列表可以为空。

    羊羽shine
  • go语言基础8-函数式编程

    吐吐吐吐吐葡萄皮

扫码关注云+社区

领取腾讯云代金券