C++ 对象指针

news2024/11/18 17:24:43

1.对象指针的一般概念

和基本类型的变量一样,每一个对象在初始化之后都会在内存中占有一定的空间。因此,既可以通过对象名,也可以通过对象地址来访问一个对象。虽然对象同时包含了数据和函数两种成员,与一般变量略有不同,但是对象所占据的内存空间只是用于存放数据成员的,函数成员不在每一个对象中存在副本。对象指针就是用于存放对象地址的变量。 对象指针遵循一般变量指针的各种规则。

(1)声明对象指针

声明对象指针的一般语法形式为:

类名*对象指针名;

例如:

Point p1;//声明Point类的对象p1
Point *pointPtr;//声明Point类的对象指针变量PointPtr
PointPtr=&p1;//将对象p1的地址赋给PointPtr,使PointPtr指向p1

(2)使用对象指针访问对象的成员

就像通过对象名来访问对象的成员一样,使用对象指针一样可以方便地访问对象的成员,语法形式为:

对象指针名->成员名;

这种形式与“(*对象指针名).成员名”的访问形式是等价的。

【例1】使用指针来访问Point类的成员。

class Point//定义Point类
{
public://外部接口
	Point(int x=0,int y=0):x(x),y(y){}//构造函数
	int getX() { return x; }//返回x
	int getY() { return y; }//返回y
private://私有数据
	int x, y;
};

int main()
{
	Point a(4, 5);//定义并初始化对象a
	Point* p1 = &a;//定义对象指针p1,用a的地址将其初始化
	cout << p1->getX() << endl;//利用指针访问对象成员
	cout << (*p1).getX() << endl;//利用指针访问对象成员
	cout << a.getX() << endl;//利用对象名访问对象成员
	return 0;
}

运行结果:
在这里插入图片描述
分析:

对象指针在使用之前一定要先进行初始化,让它指向一个已经声明过的对象,然后再使用。通过对象指针可以访问到对象的公有成员。

【例2】将下面的错误代码改写为正确

错误代码:

class A;
class B
{
	A x;
};
class A
{
	B y;
};

错误原因:
在类A的定义尚不完善的时候B类不可以使用A类的对象。

修改后的代码;

class A;
class B
{
	A* x;
};
class A
{
	B y;
};
int main()
{
	return 0;
}

这里在类B中声明一个类A的指针,这是允许的,而类A的定义位于类B的完整定义之后,当然就可以声明B类的对象作为数据成员。

2.this指针

this指针是隐含于每一个类的非静态成员函数中的特殊指针(包括构造函数和析构函数),它用于指向当前正在被成员函数操作的对象。

【注意】this指针实际上是类成员函数的一个隐含参数。在调用类的成员函数时,当前被该成员函数所操作的对象的地址会自动作为该参数的值,传递给被调用的成员函数,这样被调函数就能通过this指针来访问当前正在被操作的对象的数据成员。对于常成员函数来说,这个隐含的参数是常指针类型的。

每次对成员函数的调用,都存在一个当前正在被成员函数操作的对象,this指针就是指向当前正在被操作的这个对象的指针。
例如:

class Point//定义Point类
{
public://外部接口
	Point(int x=0,int y=0):x(x),y(y){}//构造函数
	int getX() { return x; }//返回x
	int getY() { return y; }//返回y
private://私有数据
	int x, y;
};

int main()
{
	Point a(4, 5);//定义并初始化对象a
	Point* p1 = &a;//定义对象指针p1,用a的地址将其初始化
	cout << p1->getX() << endl;//利用指针访问对象成员
	cout << (*p1).getX() << endl;//利用指针访问对象成员
	cout << a.getX() << endl;//利用对象名访问对象成员
	return 0;
}

以上代码中的成员函数:

int getX()
{
	return x;
}

中的return x;,系统需要区分每次执行这条语句时被赋值的数据成员到底属于哪一个对象,使用的就是this指针,对系统来讲,每次调用都相当于执行的是:

return this->x;

this指针明确地指出了成员函数当前所操作的数据所属的对象。实际过程是,this指针是成员函数的一个隐含的形参,当通过一个对象调用成员函数时,系统就先将该对象的地址通过参数传递给成员函数,成员函数再对该对象的数据成员进行操作,隐含地使用了this指针。

this指针是一个指针常量,对于常成员函数来说,this同时又是一个指向常量的指针。在成员函数中,可以使用*this来标识正在调用该函数的对象。

【注意】当局部作用域中声明了与类成员同名的标识符时,对该标识符的直接引用代表的是局部作用域中所声明的标识符,这时为了访问该类成员,就可以通过this指针。

3.指向类的非静态成员的指针

类的成员自身也有一些变量、函数或者对象等,因此也可以直接将它们的地址存放到一个指针变量中,这样,就可以使指针直接指向对象的成员,进而可以通过这些指针访问对象的成员。

指向对象成员的指针使用前也要先声明,再赋值,然后引用。因此首先要声明指向该对象所在类的成员的指针。

(1)声明指针语句一般形式为:

类型说明符 类名::*指针名;//声明指向数据成员的指针
类型说明符 (类名::*指针名)(参数表)//声明指向函数成员的指针

声明了指向成员的指针之后,需要对其进行赋值,也就是要确定指向类的哪一个成员。

(2)对数据成员指针赋值的语法形式一般为:

指针名=&类名::数据成员名;

(3)使用成员函数指针访问成员函数

【注意】对类成员取地址时,也要遵守访问权限的约定,也就是说,在一个类的作用域之外不能对它的私有成员取地址。

对一个普通变量,用“&”运算符就可以得到它的地址,将相应的地址赋值给一个指针就可以通过指针访问变量。但是对于类的成员来说,问题就比较复杂。

类的定义只确定了各个数据成员的类型,所占内存大小以及它们的相对位置,在定义时并不为数据成员分配相应的地址。因此上述赋值之后,只说明了被赋值的指针是专门用于指向哪个数据成员的,同时指针中存放该数据成员在类中的相对位置(即相对于起始地址的地址偏移量),当然通过这样的指针现在并不能访问什么。

由于类是通过对象而实例化的,在声明类的对象时才会为具体的对象分配内存空间,这时只要将对象在内存中的起始地址与成员指针中存放的相对偏移结合起来就可以访问到对象的数据成员了。访问数据成员时,这种结合可通过以下两种语法形式实现:

对象名.*类成员指针名;

对象指针名->*类成员指针名;

【例】访问对象的公有成员的不同方式

class Point//定义Point类
{
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) {}//构造函数
	int getX() { return x; }//返回x
	int getY() { return y; }//返回y
private://私有数据
	int x, y;
};

int main()
{
	Point a(4, 5);//定义并初始化对象a
	Point* p1 = &a;//定义对象指针p1,用a的地址将其初始化
	int(Point:: * fun)() = &Point::getX;//定义成员函数指针并初始化

	cout << (a.*fun)() << endl;//使用成员函数指针和对象名访问成员函数
	cout << (p1->*fun)() << endl;//使用成员函数指针和对象指针访问成员函数
	cout << a.getX() << endl;//使用对象名访问成员函数
	cout << p1->getX() << endl;//使用对象指针访问成员函数
	return 0;
}

运行结果:
在这里插入图片描述

(4)对成员函数指针赋值
成员函数指针在声明之后要用以下形式的语句对其赋值:

指针名=&类名::函数成员名;

【注意】常成员函数与普通成员函数具有不同的类型,因此能够被常成员函数赋值的指针,需要在声明时明确写上const关键字。

(5)使用成员函数指针调用成员函数

一个普通函数的函数名就表示它的起始地址,将起始地址赋给指针,就可以通过指针调用函数。类的成员函数虽然并不在每个对象中复制一份副本,但是由于需要确定this指针,因而必须通过对象来调用非静态成员函数。因此,上述对成员函数指针赋值以后,也还不能用指针直接调用成员函数,而是需要先声明类的对象,然后用以下的形式语句利用指针调用成员函数:

(对象名.*类成员函数指针名)(参数表)

(对象指针名->*类成员函数指针名)(参数表)

成员函数指针的声明、赋值和使用过程中的返回值类型、函数参数一定要互相匹配。

3.指向类的静态成员的指针

对类的静态成员的访问是不依赖于对象的,因此可以通过普通的指针来指向和访问静态成员。

【例1】通过指针访问类的静态数据成员。

class Point//定义Point类
{
public://外部接口
	static int count;//声明静态数据成员,用于记录点的个数
	Point(int x = 0, int y = 0) :x(x), y(y) //构造函数
	{
		count++;
	}
	Point(const Point& p) :x(p.x), y(p.y)
	{
		count++;
	}
	~Point()
	{
		count--;
	}
	int getX() { return x; }//返回x
	int getY() { return y; }//返回y

private://私有数据
	int x, y;
};

int Point::count = 0;//静态数据成员定义和初始化,使用类名限定

int main()
{
	int* p = &Point::count;//定义一个int型指针,指向类的静态成员
	Point a(4, 5);//定义并初始化对象a
	cout << "点A为:(" << a.getX() << "," << a.getY() << ")  ";
	cout << "对象的个数=" << *p << endl;//直接通过指针访问静态数据成员
	Point b(a);//定义对象b,并用a对象对其进行初始化
	cout << "点B为:(" << b.getX() << "," << b.getY() << ")  ";
	cout << "对象的个数=" << *p << endl;//直接通过指针访问静态数据成员
	
	return 0;
}

运行结果:
在这里插入图片描述
【例2】通过指针访问类的静态函数成员。

class Point//定义Point类
{
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) //构造函数
	{
		count++;
	}
	Point(const Point& p) :x(p.x), y(p.y)
	{
		count++;
	}
	~Point()
	{
		count--;
	}
	int getX() { return x; }//返回x
	int getY() { return y; }//返回y

	static void ShowCount()//输出静态数据成员
	{
		cout << "  对象的个数=" << count << endl;
	}

private://私有数据
	int x, y;
	static int count;//静态数据成员声明,用于记录点的个数
};

int Point::count = 0;//静态数据成员定义和初始化,使用类名限定

int main()
{
	void(*fun)() = Point::ShowCount;//定义一个指向函数的指针,指向类的静态成员函数
	
	Point a(4, 5);//定义并初始化对象a
	cout << "点A为:(" << a.getX() << "," << a.getY() << ")  ";
	fun();//输出对象个数,直接通过指针访问静态函数成员
	Point b(a);//定义对象b,并用a对象对其进行初始化
	cout << "点B为:(" << b.getX() << "," << b.getY() << ")  ";
	fun();//输出对象个数,直接通过指针访问静态函数成员

	return 0;
}

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/836933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8.4作业

用信号量的方式实现打印1234567后打印7654321循环交替打印。 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<head.h> char buf[]"1234567"; sem_t sem; void *callBack1(void *arg) {int i0;int sstrlen(buf)-1;while…

Arthas常用命令之trace、watch、jad、mc

目录 一、Arthas是什么 二、Arthas能解决哪些问题 三、Arthas安装启动 3.1 环境说明 3.2 下载地址 3.3 快速使用 四、Arthas基础命令 五、Arthas查看jvm相关数据 六、Arthas搬砖过程中的常用命令 6.0 查看已加载的类信息 6.1 查看已加载类中的方法信息 6.2 反编译…

面向新能源智能汽车的新基建

一&#xff0e;什么是未来所需要的新基建&#xff1f; 二&#xff0e;如何正确认识新基建&#xff1f; 三&#xff0e;如何实现新基建有效投资/最大化新基建后期价值&#xff1f; 1、什么是面向新能源智能汽车的基础设施体系&#xff1f; 汽车电动化、智能化离不开新型基础设…

Dockerfile构建lamp镜像

1、构建目录 [rootdocker ~]# mkdir compose_lamp [rootdocker ~]# cd compose_lamp/ 2、编写Docekerfile [rootdocker compose_lamp]# vim Dockerfile #基础镜像 FROM centos:7#维护该镜像的用户信息 MAINTAINER Crushlinux <crushlinux163.com>#安装httpd RUN yum -…

企业新片场排名如何优化

企业新片场排名如何优化 要如何去做关键SEO&#xff1f;第一个我们要做的就是做好 SEO 关键词的选词&#xff0c;一般就是会有第一个常用的选词方法&#xff0c;第一是以常用的提问词去做&#xff0c;不实像是情人节买什么礼物&#xff0c;母亲节买什么礼物&#xff0c; 618 有…

c++类的继承和派生、继承和访问权限、子类的构造函数、子类型、二义性问题、多重继承、虚基类、以及常见错误

继承和派生 现实写照 父亲“派生”出儿子 儿子“继承”自父亲 派生和派生&#xff0c;本质是相同的&#xff0c;只是从不同的角度来描述。 继承和派生在UML 中的表示 注意是“空心三角箭头”&#xff0c;从子类【派生的类】指向父类【被继承的类】 父类&#xff0c;也称为“基…

【Linux后端服务器开发】Reactor模式实现网络计算器

目录 一、Reactor模式概述 二、日志模块&#xff1a;Log.hpp 三、TCP连接模块&#xff1a;Sock.hpp 四、非阻塞通信模块&#xff1a;Util.hpp 五、多路复用I/O模块&#xff1a;Epoller.hpp 六、协议定制模块&#xff1a;Protocol.hpp 七、服务器模块&#xff1a;Server.…

java实现钉钉群机器人@机器人获取信息后,机器人回复(机器人接收消息)

1.需求 鉴于需要使用钉钉群机器人回复&#xff0c;人们提出的问题&#xff0c;需要识别提出的问题中的关键词&#xff0c;后端进行处理实现对应的业务逻辑 2.实现方式 用户群机器人&#xff0c;附带提出的问题&#xff0c;后端接收消息后识别消息内容&#xff0c;读取到关键…

G-channel 实现低光图像增强

G-channel 之前研究低光图像增强时&#xff0c;看到一篇博客&#xff0c;里面介绍了一种方法&#xff0c;没有说明出处&#xff0c;也没有说明方法的名字&#xff0c;这里暂时叫做 G-channel 算法。 博客地址&#xff1a;低照度图像增强&#xff08;附步骤及源码&#xff09;…

VS2019/2022 开发CAD ObjectArx 2016

开发版本配置图 ObjectARX开发VC版本对照表R14~AutoCAD2024 下载地址 Visual Studio 2012/2013下载地址 CAD 版本Wizard和SDK安装包 ObjectARX官方下载地址(不定期更新) 序言 写这篇文章是想记录一下我的配置历程&#xff0c;因为我的电脑C盘空间不足以安装太多的CAD版本和…

详解AMQP协议

目录 1.概述 1.1.简介 1.2.抽象模型 2.spring中的amqp 2.1.spring amqp 2.2.spring boot amqp 1.概述 1.1.简介 AMQP&#xff0c;Advanced Message Queuing Protocol&#xff0c;高级消息队列协议。 百度百科上的介绍&#xff1a; 一个提供统一消息服务的应用层标准高…

爬虫---练习源码

选取的是网上对一些球员的评价&#xff0c;来评选谁更加伟大一点 import csv import requests import re import timedef main(page):url fhttps://tieba.baidu.com/p/7882177660?pn{page}headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/53…

Python:判断一个数是否为质数或者输出100以内的质数

质数 质数又称素数。一个大于1的自然数&#xff0c;除了1和它自身外&#xff0c;不能被其他自然数整除的数叫做质数。 如&#xff1a;2&#xff0c;3&#xff0c;5&#xff0c;7。。。 判断一个数是否为质数 range(2, n)&#xff1a;范围在2~n&#xff08;不包括n&#xff09;之…

Mock.js的基本使用方法

官网网址&#xff1a;Mock.js (mockjs.com) 当前端工程师需要独立于后端并行开发时&#xff0c;后端接口还没有完成&#xff0c;那么前端怎么获取数据&#xff1f; 这时可以考虑前端搭建web server自己模拟假数据&#xff0c;这里我们选第三方库mockjs用来生成随机数据&#xf…

Java阶段五Day16

Java阶段五Day16 文章目录 Java阶段五Day16问题解析启动servlet冲突问题nacos注册中心用户信息验证失败前端效果不对前端请求到后台服务的流转过程 远程dubbo调用业务需求dubbo配置xml配置domain层代码 补充远程调用 师傅详情接口抽象开发WorkderServerControllerWorkerServerS…

一百四十二、Linux——查看Linux服务器架构的版本类型

一、目的 查看已经安装好的Linux服务器架构的版本类型&#xff0c;看服务器版本是32位还是64位 而且可以区分出是kettle的文件x86或x86_64&#xff0c;x86是32位&#xff0c;而x86_64是64位 注意&#xff1a; 32位的查询结果为i386、i686 64位的查询结果为x86_64 二、Linu…

idea配置docker部署

安装docker插件 setting -> plugins 配置docker远程连接 参考&#xff1a;docker配置远程连接端口 https://blog.csdn.net/jinying_51eqhappy/article/details/132103423?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%221…

探索CSS计数器:优雅管理网页元素的计数与序号

113. 探索CSS计数器&#xff1a;优雅管理网页元素的计数与序号 在前端开发中&#xff0c;我们经常需要对网页元素进行计数与序号&#xff0c;如有序列表、表格行号、步骤指示等。为了优雅地管理这些计数与序号&#xff0c;CSS提供了一种强大的功能&#xff1a;CSS计数器&#…

PHP实现首字母头像

<?php $name"哈哈"; $logoletter_avatar($name);echo <img src".$logo." style" border-radius: 50%;">;function letter_avatar($text) {$total unpack(L, hash(adler32, $text, true))[1];$hue $total % 360;list($r, $g, $b) hs…

【2023】Git版本控制-远程仓库详解

目录 创建远程仓库向远程仓库推送数据文件从第二台主机本地拉取远程仓库数据第一台主机同步远程仓库数据tag标签git忽略文件 Git远程仓库是Git版本控制系统的一个概念&#xff0c;它是一个存储Git代码的远程服务器。 你可以将本地Git仓库上传到远程仓库&#xff0c;以便与其他…