在C++学习中,指针是一个用于指向另一个变量的地址的变量。理解指针有一定难度,但是理解它的工作原理后,会发现它们是非常强大和有用的工具。指针可以用来指向一般的变量,也可以指向对象。
一、指向对象的指针
在建立对象时,编译系统会为每一个对象分配一定的存储空间,以存放其成员。对象空间的起始地址就是对象的指针。
定义指向类对象的指针变量的一般形式为:
类名 *对象指针名
首先创建一个类,代码如下:
class Timer{
public:
int hour;
int minute;
int second;
Timer(){
hour = 0;
minute = 0;
second = 0;
}
Timer(int h, int m, int s): hour(h), minute(m), second(s){}
void show_time(){
cout <<hour <<':' <<minute <<':' <<second <<endl;
}
};
定义指向Timer类对象的指针变量,代码如下:
int main(){
Timer *pt, t1;
pt = &t1;
return 0;
}
通过对象指针访问对象成员,代码如下:
int main(){
Timer *pt, t1;
pt = &t1;
// 直接访问hour
cout <<"hour:" <<pt->hour <<endl;
cout <<"hour:" <<(*pt).hour <<endl;
// 访问成员函数
pt->show_time();
(*pt).show_time();
return 0;
}
运行结果如下:
- pt->hour这种方式是通过 "->"运算符直接调用指针pt所指向对象的hour成员变量。
- (*pt).hour这种方式是通过解引用 *pt 来获取指针pt所指向的对象,然后通过点"."运算符来调用该对象的hour成员变量。
这两种访问指针所指向对象的成员函数的等价方式,pt是指向Timer对象的指针。大多情况下,使用pt->hour和pt->show_time()的方式更为简洁和直观。
二、指向对象成员的指针
对象有地址,对象中的成员也有地址,存放对象成员地址的指针就是指向对象成员的指针变量。
定义指向对象数据成员的指针变量的一般形式:
数据类型名 *指针变量名
在类外通过指向对象数据成员的指针变量访问对象数据成员hour,代码如下:
int main(){
Timer t1;
int *ph;
// 指向Timer对象数据成员hour
ph = &t1.hour;
// 输出ph指针指向的地址
cout <<"hour:" <<ph <<endl;
// 输出ph指针指向数据成员变量值
cout <<"hour:" <<*ph <<endl;
return 0;
}
从运行结果上可以看出,现在所输出的ph是指向Timer成员变量hour的地址,通过*ph则可以得到该成员变量的值,如下图:
定义指向普通函数的指针变量方法的形式:
数据类型名 (*指针变量名)(参数列表)
这里定义一个普通函数,并通过指针指向它,代码如下:
#include <iostream>
using namespace std;
void sortArray(int arr[], int size){
cout <<"Array:" <<&arr <<", size:" <<size <<endl;
}
int main(){
// 声明p指向void型函数的指针变量
void (*p)(int arr[], int size);
// 将 sortArray函数的入口地址赋给指针变量p
p = sortArray;
// 定义参数列表数组
int nums[3] = {2, 3, 5};
// 调用sortArray函数
(*p)(nums, 3);
return 0;
}
输出结果如下图:
而定义一个指向对象成员函数的指针变量则比较复杂一些,和定义指向普通函数的指针变量有所不同。编译系统要求在上面的赋值语句中,指针变量的类型必须与赋值好右侧函数的类型相匹配,主要以下三个方法:
- 函数参数的类型和参数个数;
- 函数返回值的类型;
- 所属的类;
定义指向公用成员函数的指针变量的一般形式:
数据类型名 (类名:: *指针变量名) (参数列表)
使指针变量指向一个公用成员函数的一般形式:
指针变量名 = &类名::成员函数名
代码示例如下:
// 定义指向Timer类公用成员函数的指针变量
void (Timer::*ps)();
// 指针变量指向Timer类公用成员函数show_time
ps = &Timer::show_time;
// 调用对象t1中ps指向的成员函数
(t1.*ps)();
运行结果如下:
其实,以上定义成员函数指针变量同时,可以直接赋值Timer类成员函数址地,其得到结果是一样的。代码如下 :
// 定义指向Timer类公用成员函数的指针变量,并且指针变量指向Timer类公用成员函数show_time
void (Timer::*ps)() = &Timer::show_time;
// 调用对象t1中ps指向的成员函数
(t1.*ps)();
成员函数不是存放在对象的空间中,而是存放在对象外的空间中,如果多个同类的对象,它们是可以共用同一个函数代码段。因此,赋给变量ps的是这个公用的函数代码段的入口地址。
三、对象数组的指针
在C++中,处理数组和指向数组的指针时,可以通过“引用”或“指针”到数组的方式。在上一篇中获取对象数组长度时,也讲到对象数组相关内容。
址地:C++面向对象程序设计 - 数组与sizeof、对象指针使用-CSDN博客
先通过一段代码了解下:
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *ref = arr;
// 或
int (*ref)[10] = &arr; //通过指针方式
// 或
int (&ref)[10] = arr; //通过引用方式
1、 ref是一个指向整数的指针,当把数组arr赋值给ref时,arr会自动转换为指向数组第一个元素的指针(即&arr[0])。现在ref指向数组的第一个元素,即整数值1.可以通过解引用ref或者进行指针算术来访问数组其他元素。示例代码如下:
#include <iostream>
using namespace std;
int main(){
int arr[10] = {1, 5, 3, 4, 5, 6, 7, 8, 9, 10};
int *ref = arr;
cout <<"first value:" <<*ref <<endl;
ref++;
cout <<"seocnd value:" <<*ref <<endl;
return 0;
}
运行结果可以看出当指针移到下个位置时,则对接数组arr第二个元素(即&arr[1]),如下图:
2、 *ref 是一个指向含有10个整数的数组的指针;&arr取得了整个数组arr地址并将它赋值给ref来初始化它;可以通过解引用ref得到整个数组,而不仅仅是数组第一个元素。示例代码如下 :
#include <iostream>
using namespace std;
int main(){
int arr[10] = {1, 5, 3, 4, 5, 6, 7, 8, 9, 10};
int (*ref)[10] = &arr;
cout <<"first value:" <<(*ref)[0] <<endl;
cout <<"seocnd value:" <<(*ref)[1] <<endl;
return 0;
}
以上代码可以看出,这里ref不再指向某个子元素,而是整个数组arr,所以需要通过索引进行取值。结果如下图:
3、 &ref实际上是一个数组的引用,而不是指针。arr是数组的名称,它是数组本身的引用,所以ref是数组arr的一个别名,他们指向相同的内存位置,这里的ref也能直接访问数组的元素。示例代码如下:
#include <iostream>
using namespace std;
int main(){
int arr[10] = {1, 5, 3, 4, 5, 6, 7, 8, 9, 10};
int (&ref)[10] = arr;
cout <<"first value:" <<ref[0] <<endl;
cout <<"seocnd value:" <<ref[1] <<endl;
return 0;
}
这里ref只是数组arr的一个别名,所以从代码中可以看出,直接使用ref取值即可。结果如下图:
在大多数情况下,数组的引用和指向数组的指针,在用法上很相似,但它们在类型上是有区别的。指针是一个变量,可以指向不同的地址。而引用则是一别名,必须在声明时初始化,不能重新绑定到另一个对象。
四、this指针
每个成员函数中都包含一个特殊的指针,这个指针名是固定的,称为this。它是指向本类对象的指针,它可以调用本类中成员。this指针是隐式使用的,它是作为参数被传递给成员函数的。
之前创建对象,定义构造函数时,形参都是以缩写方式声明的,如与成员变量一致,编译系统会直接报错。有人可能在想,就是想使用全称来声明形参,则可以通过显示使用this指针,解决这一问题。代码如下:
class Timer{
public:
int hour;
int minute;
int second;
// this指针使用 -> 运算符调用成员
Timer(){
this->hour = 0;
this->minute = 0;
this->second = 0;
}
// *this指针通过点调用成员
Timer(int hour, int minute, int second){
(*this).hour = hour;
(*this).minute = minute;
(*this).second = second;
}
void show_time(){
cout <<hour <<':' <<minute <<':' <<second <<endl;
}
};
此时编译程序,则不会报错了。以上代码中无参构造函数中直接使用 -> 运算符调用成员变量,和有参构造函数中*this通过点调用成员变量方式,在“一、指向对象的指针”中已说明,这里不再阐述。