C++ 类成员指针

1.简介

成员指针是 C++ 引入的一种新机制,是指向类成员的指针,你可以使用它们来访问类的成员变量和成员函数,包括公有、私有和保护成员。它的申明方式和使用方式都与一般的指针有所不同。

成员指针分为成员变量指针和成员函数指针。

2.成员变量指针

一个类对象生成后,它的某个成员变量的地址实际上由两个因素决定:对象的首地址和该成员变量在对象之内的偏移量。

成员变量指针用来保存类的某个数据成员在类对象内的偏移量。它只能用于类的非静态成员变量。

成员变量指针的定义格式:

成员类型 类名::*指针名=&类名::成员名;

成员变量指针使用示例:

#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的友元函数是不行的,因为是在调用 printArray 函数时访问了类ArrayClass的私有成员,而不是在 printArray 函数体内用到类 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()来访问私有成员
}

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

3.成员函数指针

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

成员函数指针的定义格式:

成员函数返回类型 (类名::*指针名)(形参)= &类名::成员函数名

成员指针使用示例:

#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)为成员函数指针赋值时,需要显示使用&运算符,不能直接将“类名::成员函数名”赋给成员函数指针。

4.小结

成员指针是一个强大的工具,可以用于实现多态性、回调机制和动态方法调用等高级功能。但请谨慎使用它们,因为它们可以导致复杂的代码,容易出错。在大多数情况下,C++的标准库和现代C++特性(如 lambda 表达式、智能指针等)可以更安全地实现相同的功能。

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值