文章目录
指针常量(Constant Pointer)和常量指针(Pointer to Constant)是C++中两种不同类型的指针,它们的主要区别在于指针本身的常量性和指针所指向的数据的常量性。
指针常量(Constant Pointer)
由于引用这个语法糖的存在(引用本质就是指针常量),所以指针常量用得很多(个人感觉)
指针常量是指指针本身的值(即存储的地址)是常量,不能被改变。但是,通过这个指针指向的数据的值是可以修改的。
的声明方式是 数据类型 *const 变量名
。例如,int *const p
表示 p
是一个指向 int
类型数据的指针常量,它的值一旦被赋值后就不能改变,但所指向的内容可以修改。
一句话总结:指针常量,顾名思义,拿指针来修饰一个常量,*
+ const p
。既然const
修饰了p
,也就是说地址不能改变,地址中的值却是可以修改。
int value = 10;
int* const ptr = &value; //指针本身是常量,指向的值可修改
在这个例子中,ptr
是一个指向int类型的指针常量,它的值(即value
的地址)不能被改变,但是可以通过ptr
来修改value的值。
*ptr = 20; // 合法,可以修改ptr指向的值
// ptr = &otherValue; // 非法,不能修改ptr本身的值
常量指针(Pointer to Constant)
常量指针是指指针所指向的数据是常量,不能被改变,但是指针本身的值(即它所存储的地址)是可以修改的。这意味着你可以改变指针指向的地址,但不能通过这个指针来修改所指向地址的数据。
常量指针的声明方式可以是 数据类型 const *变量名
或者 const 数据类型 *变量名
。例如,const int *p
表示 p
是一个指向 int
类型常量的指针,它的值可以改变以指向其他地址,但所指向的内容不可修改。
一句话总结:指针常量,顾名思义,拿常量来修饰一个指针,它的定义方式就是const
+指针*p
。由于修饰的指针形式是*p
,就理解成解引用的值,const
修饰一个具体的值,也就是说它肯定是指这个指针指向的值不能改变,那么地址可以修改。
int value = 10;
const int* ptr = &value; //int const *ptr = &value;
在这个例子中,ptr
是一个指向int
类型的常量指针,它指向value
,但不能通过ptr
来修改value
的值。
// *ptr = 20; // 非法,不能通过常量指针修改其指向的值
int otherValue = 30;
ptr = &otherValue; // 合法,可以修改ptr本身的值
指针常量和常量指针总结
指针常量(Constant Pointer): 拿指针修饰了一个常量,这个地址不能改变,因为这个常量本质是指针。指针的地址是固定的,不能指向别的地址,但可以修改其指向的数据的值。
常量指针(Pointer to Constant): 拿常量修饰指针,由于后面的指针*p是解引用的形式,所以指针里的值不能改变,但是它的地址可以改变。指针可以指向不同的地址,但不能修改其指向的数据的值。
函数指针
函数指针的定义
函数指针是指向函数的指针变量。它可以⽤于存储函数的地址,允许在运⾏时动态选择要调⽤的函数。
在C++中,函数指针可以用来实现回调函数、策略模式、命令模式等,允许在运行时选择要调用的函数。
定义函数指针
假设函数原型如下:
int add(int x, int y) {
return x + y;
}
int subtract(int x, int y) {
return x - y;
}
对应的函数指针定义如下:
//返回类型 (*指针变量名)(参数类型1, 参数类型2, ...);
int (*ptrToAdd)(int, int);
调用方法
将函数的地址赋给函数指针,并通过函数指针来调用该函数。其实函数也是通过内存地址来储存的,函数名可以用作地址。
int main() {
// 定义一个函数指针,指向一个接受两个int参数、返回int的函数
int (*operation)(int, int);
int a = 5, b = 3;
// 将函数指针指向加法函数,并调用
operation = add;
std::cout << "Adding: " << operation(a, b) << std::endl;
// 将函数指针指向减法函数,并调用
operation = subtract;
std::cout << "Subtracting: " << operation(a, b) << std::endl;
return 0;
}
函数指针使用场景
- 回调函数:在事件驱动编程或异步编程中,回调函数是根据事件的发生来被调用的函数。函数指针允许动态指定回调函数。
- 策略模式:在设计模式中,策略模式允许在运行时选择算法或行为。函数指针可以用来实现这一点,通过改变指针指向不同的函数来更换算法或策略。
- 接口表:在C语言中,函数指针数组常用于创建接口表,这样可以根据索引调用不同的函数,从而实现类似于面向对象编程中的多态性。
- 简化条件语句:在需要基于条件执行不同函数的场景中,可以使用函数指针数组来代替大量的if-else或switch-case语句,使代码更加清晰和简洁。
- 函数指针作为参数实现可插拔的函数行为: 可以将函数指针作为参数传递给其他函数,实现⼀种可插拔的函数⾏为。
记不住?总之一句话:函数指针可以实现在运⾏时动态选择要调⽤的函数,我觉得也可以这样回答:
- 当你希望一个函数能够执行不同的操作,而这些操作在编写代码时可能未知或者会经常更改。
- 当你需要根据不同的场景选择不同的算法或策略时。
- 在需要回调函数的场景,比如事件处理器、定时器或线程的启动函数。
案例
计时器回调
实现一个简单的计时器功能,当计时器超时时,它会调用一个指定的回调函数。这里,我们将创建一个Timer类,它接受一个回调函数指针和一个指定的延迟时间(以秒为单位)。当时间到达时,回调函数被调用。
在这个例子中:
Timer
类接受一个函数指针m_callback
和一个延迟时间m_seconds
作为构造函数的参数。start
方法会使当前线程休眠指定的秒数,然后调用回调函数。timeoutCallback
是一个简单的回调函数,当计时器到达指定时间时被调用。- 在
main
函数中,我们创建了一个Timer
实例,设置了3秒的超时时间,并传递了timeoutCallback
作为回调函数。调用start
方法后,程序将等待3秒,然后输出一条消息表示计时器已超时。
通过这种方式,函数指针作为回调提供了一种灵活的机制来响应特定事件,这在很多异步处理和事件驱动的编程场景中非常有用。
#include <iostream>
#include <thread>
#include <chrono>
// 定义回调函数类型
typedef void (*Callback)();
// Timer类,用于执行回调
class Timer {
public:
Timer(Callback callback, unsigned int seconds)
: m_callback(callback), m_seconds(seconds) {}
// 开始计时器
void start() {
std::this_thread::sleep_for(std::chrono::seconds(m_seconds));
m_callback(); // 调用回调函数
}
private:
Callback m_callback; // 回调函数指针
unsigned int m_seconds; // 延迟时间
};
// 回调函数实现
void timeoutCallback() {
std::cout << "Timeout! Timer has expired." << std::endl;
}
int main() {
std::cout << "Timer started, waiting for timeout..." << std::endl;
Timer timer(timeoutCallback, 3); // 创建一个3秒的计时器
timer.start(); // 启动计时器
return 0;
}
指针函数
指针函数是一种返回指针类型的函数。这意味着该函数的执行结果是一个指针,指向某种类型的数据。指针函数在很多场景下都很有用,尤其是当你需要从函数中返回动态分配的内存或者数组、字符串等数据结构时。
定义方法
返回类型* 函数名(参数列表);
使用场景
- 动态内存分配:当你需要在函数内部动态分配内存,并将其地址返回给调用者。
- 数组和字符串返回:当函数需要返回一个数组或字符串,而这个数组或字符串是在函数内部定义或生成的。
- 数据结构操作:当函数操作某种数据结构(如链表、树等)的节点,并需要返回指向这些节点的指针时。
案例
动态内存分配和数据结构返回
在这个示例中,createArray函数动态创建了一个整数数组,并用每个元素的索引的平方来初始化数组。这个函数返回一个指向数组的指针,允许调用者访问和操作这些元素。通过返回指针,函数能够让调用者访问在函数内部创建或修改的数据结构,同时也传达了内存管理的责任,因为调用者需要确保适时释放分配的内存。
#include <iostream>
using namespace std;
// 指针函数,动态分配一个整数数组,并返回其指针
int* createArray(int size) {
int* arr = new int[size];
for (int i = 0; i < size; ++i) {
arr[i] = i * i; // 示例初始化
}
return arr; // 返回指向动态分配数组的指针
}
int main() {
int* myArray = createArray(10); // 调用指针函数
for (int i = 0; i < 10; ++i) {
cout << myArray[i] << " ";
}
cout << endl;
delete[] myArray; // 释放动态分配的内存
return 0;
}
函数指针和指针函数的总结
函数指针是指向函数的指针变量。可以存储特定函数的地址,并在运⾏时动态选择要调⽤的函数。通常⽤于回调函 数、动态加载库时的函数调⽤等场景。
int add(int a, int b)
{
return a + b;
}
//函数指针指向add函数
int (*ptr)(int, int) = add; //int (*ptr)(int, int) = &add也可以。
int result = (*ptr)(3, 4); //ptr(3, 4)也可以,其实就是一个解引用。
指针函数是⼀个返回指针类型的函数,⽤于返回指向某种类型的数据的指针。
int* getPointer(){
int x = 10;
return &x; //返回局部变量地址,不建议这么做
}