C++语法|指向类成员(成员变量和成员方法)的指针及其相关应用场景
文章目录
- 1.基本语法
- 指向成员变量的指针
- 示例
- 指向成员函数的指针
- 示例
- 注意事项
- 2.应用场景
- 泛型编程和模板:通用成员访问打印函数
- 回调机制和事件处理:基于简单GUI框架的事件处理
1.基本语法
指向类成员的指针是一种特殊的指针类型,用于指向类的成员变量或成员函数。与普通指针不同,指向类成员的指针不能单独使用,必须结合特定的类对象来进行访问。
指向成员变量的指针
指向成员变量的指针用于指向某个类的成员变量。其语法如下(重点是要声明作用域):
class A {
public:MemberType member;
};// 定义指向成员变量的指针
MemberType A::*ptr = &A::member;
示例
#include <iostream>
using namespace std;
class MyClass {
public:int data;
}int main() {MyClass obj;obj.data = 10;//定义一个指向成员变量的指针int MyClass::*ptr = &MyClass::data;cout << "data: " << pbj.data << endl;cout << "data using pointer: " << obj.*ptr << endl;
}
指向成员函数的指针
class A {
public:ReturnType Function(Arg);
};// 定义指向成员函数的指针
ReturnType (A::*pointerToFunction)(Arg) = &A::Function;
示例
#include <iostream>
using namespace std;
class MyClass {
public:void display() {cout << "Hello, World!" << endl;}
};
int main () {MyClass obj;void (MyClass::*funcPtr)() = &MyClass::display;//通过对象调用成员函数(obj.*funcPtr)();//通过指针调用成员函数MyClass* objPtr = &obj;(objPtr->*funcPtr)();return 0;
}
注意事项
我们定义一个包含成员方法、静态成员方法、成员变量的类:
一:如何通过指向类成员变量的指针来访问类成员
class Test {
public:void func() { cout << "call Test::func" << endl; }static void static_func() { cout << "Test::static_func" << endl; }static int mb;int ma;
};
int main () {Test t1;//int *p = &Test::ma //报错 无法从“int Test::* ” 转换成 "int *"int Test::*p = &Test::ma;//*p = 20; 报错,因为我们都没有初始化一个 Test对象,类不能脱离对象谈成员//应该先进行初始化,也就是在前面加上 Test t1;t1.*p = 20;
}
总结:普通的成员变量必须依赖一个对象,所以即使操作外部指针也应该依赖一个对象,当然如果我们操作一个静态成员变量,它是不依赖对象的,跟普通指针使用一样
int Test::mb;
int main () {int *p1 = &Test::mb;*p1 = 40; //mb为40
}
二、指向成员方法的指针
我们有一个普通的成员方法void func()我们如何定义一个函数指针来指向这个函数呢?
int main () {//如果是普通函数中是可以的,但是C++中不被允许//无法从"void (thiscall Test::*)(void)"转换为"void (__cdecl *)(void)"//void(*pfunc)() = &Test::func;//(*pfunc)();Test t1;Test *t2 = new Test();void (Test::*pfunc)() = &Test::func;(t1.*pfunc)(); //通过解引用来调用(t2->*pfunc)();
}
三、指向静态成员方法的指针
跟之前的静态成员变量一样,可以当做一个普通指针来使用,不需要初始化一个类。
静态成员方法的调用不需要依赖一个对象,他就是一个普通的C函数,只不过现在落在了类的作用域里面而已。
2.应用场景
泛型编程和模板:通用成员访问打印函数
在模板编程中,指向成员的指针可以用于编写通用代码,能够操作不同类型对象的成员。这在编写需要操作不同类型对象的函数模板时非常有用。
假设我们有几个不同的类,每个类都有各自不同的成员变量。我们希望编写一个通用的函数,该函数可以接受任何对象和该对象类的任何成员变量的指针,并打印出该成员的值。这种方法在处理多种数据类型的统一处理程序(如日志、序列化、UI显示等)时非常有用。
- 首先我们定义两个示例类:
// 定义两个示例类
class Person {
public:string name;int age;Person(const string& name, int age) : name(name), age(age) {}
};class Product {
public:string title;double price;Product(const string& title, double price) : title(title), price(price) {}
};
- 我们想定义一个通用的打印函数,可以打印不同对象的不同成员
int main() {Person alice("Alice", 30);Product laptop("Laptop", 999.99);// 打印不同对象的不同成员display(alice, &Person::name); // 打印 Person 对象的 name 成员display(alice, &Person::age); // 打印 Person 对象的 age 成员display(laptop, &Product::title); // 打印 Product 对象的 title 成员display(laptop, &Product::price); // 打印 Product 对象的 price 成员return 0;
}
你能帮我编写出来吗?
答案:
//通用的打印函数模板
template<typename T, typename M>
void display(const T&obj, M T::*member) {cout << obj.*member << endl;
}
- 灵活性:printMember 函数可以用于任何类和任何类型的成员变量。
- 重用性:不需要为每种类型或每个成员单独编写打印逻辑。
- 简洁性:代码更加简洁和容易维护,因为成员访问逻辑只编写一次。
回调机制和事件处理:基于简单GUI框架的事件处理
假设我们正在开发一个GUI应用程序,其中包含多个按钮,每个按钮点击时都需要执行不同的操作。我们可以定义一个Button类,该类包含一个成员函数指针,当按钮被点击时,该指针指向的成员函数将被调用。
Step 1: 定义Button类
首先,我们定义一个Button类,它包含一个指向成员函数的指针作为回调。
我们需要定义 Button 类以存储指向成员函数的指针和一个指向调用它的对象的指针。这里的一个关键点是确保成员函数指针的类型正确,并且我们还需要知道哪个类的成员函数将被调用。下面是如何实现这个:
template <typename T>
class Button {
public:using EventCallback = void (T::*)(); // 成员函数指针类型Button() : callbackObject(nullptr), onClick(nullptr) {}// 设置回调函数及其关联的对象void setOnClick(EventCallback callback, T* obj) {onClick = callback;callbackObject = obj;}// 模拟按钮点击void click() {if (callbackObject && onClick) {std::cout << "Button clicked.\n";(callbackObject->*onClick)(); // 使用成员函数指针调用函数}}private:T* callbackObject; // 指向调用成员函数的对象的指针EventCallback onClick; // 成员函数指针
};
Step 2: 定义具体操作
我们定义一个Application类,该类包含多个成员函数,每个函数都可以作为按钮的回调。
class Application {
public:void displayMessage() {std::cout << "Displaying message!\n";}void openFile() {std::cout << "Opening file...\n";}void shutdown() {std::cout << "Shutting down...\n";}
};
Step 3: 绑定事件与成员函数
我们需要将按钮的点击事件绑定到特定的成员函数。这里我们使用std::function和std::bind来完成绑定,因为直接使用指向成员函数的指针需要处理对象的具体实例,而std::function和std::bind提供了更灵活的方式来处理这种情况。
int main() {Application app;Button<Application> btnMessage, btnFile, btnShutdown;// 设置按钮的回调btnMessage.setOnClick(&Application::displayMessage, &app);btnFile.setOnClick(&Application::openFile, &app);btnShutdown.setOnClick(&Application::shutdown, &app);// 模拟按钮点击btnMessage.click();btnFile.click();btnShutdown.click();return 0;
}
总结
这种方式直接使用模板和指向成员函数的指针。此外,它为每个按钮提供了泛型支持,这意味着 Button 类可以与任何类的任何成员函数一起使用,只要这些函数满足返回值 void ,传参()。
使用模板的另一个好处是你可以在编译时捕获类型错误,如尝试绑定到非法函数或不正确的对象。然而,这种方法的限制是它只能与无参数且返回 void 的成员函数一起工作,如果需要更复杂的回调签名,你可能需要修改模板定义来支持这些。
其实,本节比较难的应该是属于函数指针了,但是在C++中常常使用function和bind来替代函数指针的使用场景。。。
扩展阅读:
C语言学习细节|C语言面向对象编程!函数指针如何正确使用
C++语法|可调用对象与function类型
C++语法|bind和function解析并实现一个简易线程池
C++语法|bind1st和bind2nd的用法
相关文章:
C++语法|指向类成员(成员变量和成员方法)的指针及其相关应用场景
文章目录 1.基本语法指向成员变量的指针示例 指向成员函数的指针示例 注意事项 2.应用场景泛型编程和模板:通用成员访问打印函数回调机制和事件处理:基于简单GUI框架的事件处理 1.基本语法 指向类成员的指针是一种特殊的指针类型,用于指向类…...
【C语言】通讯录系统实现
目录 1、通讯录系统介绍 2、代码分装 3、代码实现步骤 3.1制作菜单函数以及游戏运行逻辑流程 3.2、封装人的信息PeoInfo以及通讯录Contact结构体类型 3.3、初始化通讯录InitContact函数 3.4、增加联系人AddContact函数 3.5、显示所有联系人ShowContact函数 3.6、删除联系人D…...
(delphi11最新学习资料) Object Pascal 学习笔记---第12章第1节 ( 类静态方法与Windows API回调)
12.1.4 类静态方法与Windows API回调 静态类方法没有隐藏的Self参数意味着静态类方法可以作为回调函数传递给操作系统(例如,在Windows上)。实际上,您可以声明一个具有stdcall调用约定的静态类方法,并将其用作直接的…...
第一个Rust程序
在安装好Rust以后,我们就可以编写程序了。 首先,我们执行下面的命令,尽量让你的rust版本和我的版本相同,或者比我的版本大。 zhangdapengzhangdapeng:~$ cargo --version cargo 1.78.0 (54d8815d0 2024-03-26) zhangdapengzhangd…...
【LInux】<基础IO> 文件操作 | 文件描述符 | 重定向
👦个人主页:Weraphael ✍🏻作者简介:目前正在学习c和算法 ✈️专栏:Linux 🐋 希望大家多多支持,咱一起进步!😁 如果文章有啥瑕疵,希望大佬指点一二 如果文章对…...
MySQL--增、删、改、查,
数据库的概述、发展、现状、历史、分类 MySQL关系型数据库、架构(C/S) window系统安装MySQL数据库 Linux系统【选学】 数据库对象——数据库(database) show、create、drop命令 数据库对象——表(tableÿ…...
5.12学习总结
一.JAVA聊天室项目 文件发送 使用 Java Socket 实现聊天内容或文件的传输的原理如下: 服务器端启动:聊天室的服务器端在指定的端口上监听客户端的连接。它创建一个 ServerSocket 对象,并通过调用 accept() 方法等待客户端的连接请求。客户…...
ansible利用playbook 部署lamp架构
搭建参考:ansible批量运维管理-CSDN博客 定义ansible主机清单 [rootansible-server ~]# vim /etc/hosts 192.168.200.129 host01 192.168.200.130 host02 [rootansible-server ~]# vim /etc/ansible/hosts [webserver] host01 host02 在ansible端编写index.html…...
SPI通信(使用SPI读写W25Q64)
SPI通信协议 • SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线 • 四根通信线: SCLK:串行时钟线,用来提供时钟信号的。 MOSI:主机输出,从机输入 MISO:从机输出,主机输入 SS:…...
<sa8650>QCX Usecase 使用详解—拓扑图 XML 定义
<sa8650>QCX Usecase 使用详解—拓扑图 XML 定义 一 、前言二、拓扑图 XML 定义2.1 <Node, port, link>2.2 < XML prolog >2.3 < UsecaseDef >2.4 < Usecase>2.5 < Targets>2.5.1 < Target>2.5.2 < Range>2.6 < Pipeline>2.…...
使用C++11实现Golang的defer功能
本文主要用C11标准来实现Golang的defer功能。 背景 目前笔者的主力语言是Golang,其次是C,再次是JS、Delphi。在Golang工程中大量使用了defer关键字实现函数的延迟调用。如打开文件的出错处理。近来在C工程中遇到类似需求,在函数返回时进行某…...
前端之电力系统SVG图低代码
其实所有的图形都是由点,线,面组成的。点线面可以组成一个设备。下面就简单讲讲点线面是怎么画的吧 对于线,可以用path <g><path:d"M ${beginX},${beginY} L ${endX},${endY}":stroke-width"lineWidth":strok…...
括号生成[中等]
优质博文:IT-BLOG-CN 一、题目 数字n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例 1: 输入:n 3 输出:["((()))","(()())","(())(…...
配置ubuntu的VNC时遇到报错_XSERVTransmkdir: Mode of /tmp/.X11-unix should be set to 1777
现在win11内嵌了ubuntu系统,我在根据打造基于 VNC 的 Ubuntu 20.04 的远程桌面 配置VNC server时,到了 vncserver :1 这一步,遇到报错: vncserver: /usr/bin/Xtigervnc did not start up, please look into /root/.vnc/xxxxx.:1.…...
openstack部署nova中出现的问题:
[rootcontroller nova]# su -s /bin/sh -c “nova-manage db sync” nova /usr/lib/python2.7/site-packages/pymysql/cursors.py:170: Warning: (1831, u’Duplicate index block_device_mapping_instance_uuid_virtual_name_device_name_idx. This is deprecated and will be…...
【OpenCV 基础知识 3】边缘检测
文章目录 cvCanny完整示例代码 cvCanny 这行代码使用OpenCV库中的 cvCanny 函数对灰度图像进行边缘检测。让我解释一下: cvCanny(gray, dst, 10, 100, 3);gray: 这是输入的灰度图像,即要进行边缘检测的图像。dst: 这是输出的边缘图像,即将结…...
拓宽知识储备量(指数级成长)
对于增强自己的知识储备,不是什么知识都往脑袋里去塞,最好的办法就是让自己的心态回到自己初心的时候,始终保值一颗学者的心,你像那些成功人士,比如格力,华为,腾讯等这样的大公司创始人哪个不是…...
x264 帧类型代价计算原理:slicetype_mb_cost 函数分析
slicetype_mb_cost 函数 函数功能 计算每个宏块 MB 的代价 cost。函数参数分析 x264_t *h:全局编码结构体x264_mb_analysis_t *a:宏块分析结构体x264_frame_t **frames:系列帧数据结构体int p0:帧序号之一,一般指向靠前帧int p1:帧序号之一,一般指向靠后帧int b:帧标志…...
战网国际服加速器哪个好用 暴雪战网免费加速器分享
战网国际服(Battle.net International或Battle.net Global)是由暴雪娱乐公司(Blizzard Entertainment)运营的面向全球玩家的多人在线游戏平台。与专注于特定地区的版本不同,国际服允许玩家不受地域限制地访问暴雪的多款…...
Java入门基础学习笔记26——break,continue
跳转关键字: break: 跳出并结束当前所在循环的执行。 continue: 用于跳出当前循环中的当次执行,直接进入循环中的下一次执行。 package cn.ensource.loop;public class BreakContinueDemo8 {public static void main(String[] a…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...
