指针
引用与指针
引用 & | 指针 * |
---|---|
必须初始化 | 可以不初始化 |
不能为空 | 可以为空 |
不能更换目标 | 可以更换目标 |
初始化案例
int &r; //不合法,没有初始化引用
int *p; //合法,但p为野指针,使用需要小心
(1)是否需要初始化
由于引用不能为空,所以我们在使用引用的时候不需要测试其合法性,而在使用指针的时候需要首先判断指针是否为空指针,否则可能会引起程序崩溃。
进行检查
void test_P(int *p){
if(p !=NULL)
*p=3;
return;
}
void test_r(int &r){
r=3;
return;
]}
(2)指向目标是否能更改
指针可以随时改变指向,但是引用只能指向初始化时指向的对象,无法改变
指向目标
int a = 1;
int b = 2;
int &r = a; //初始化引用r指向变量a
int *p = &a; //初始化指针p指向变量a
p = &b; //指针p指向了变量b
r = b; //引用r依然指向a,但a的值变成了b
引用
左值引用
常规引用,一般表示对象的身份。
右值引用
右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。
右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:
消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
能够更简洁明确地定义泛型函数。
引用折叠
X& &
、X& &&
、X&& &
可折叠成 X&
X&& &&
可折叠成 X&&
C++的引用在减少了程序员自由度的同时提升了内存操作的安全性和语义的优美性。比如引用强制要求必须初始化,可以让我们在使用引用的时候不用再去判断引用是否为空,让代码更加简洁优美,避免了指针满天飞的情形。除了这种场景之外引用还用于如下两个场景
引用型参数
一般我们使用const reference参数作为只读形参,这种情况下既可以避免参数拷贝还可以获得与传值参数一样的调用方式。
引用型参数
void test(const vector<int> &data)
{
//...
}
int main()
{
vector<int> data{1,2,3,4,5,6,7,8};
test(data);
}
引用返回值
C++提供了重载运算符的功能,我们在重载某些操作符的时候,使用引用型返回值可以获得跟该操作符原来语法相同的调用方式,保持了操作符语义的一致性。一个例子就是operator []操作符,这个操作符一般需要返回一个引用对象,才能正确的被修改。
引用返回值
vector<int> v(10);
v[5] = 10; //[]操作符返回引用,然后vector对应元素才能被修改
//如果[]操作符不返回引用而是指针的话,赋值语句则需要这样写
*v[5] = 10; //这种书写方式,完全不符合我们对[]调用的认知,容易产生误解
指针
函数指针
函数指针的定义
int (*pf)(int,int);
typedef int (*PF)(int,int);
函数指针使用
/**
* @file func_pointer.cpp
* @brief 函数指针的使用!
*/
#include <iostream>
using namespace std;
/**
* @brief
* 定义了一个变量pFun,这个变量是个指针,指向返回值为空和参数为int的函数的指针!
*/
void (*pFun)(int);
/**
* @brief 代表一种新类型,不是变量!所以与上述的pFun不一样!
*/
typedef void (*func)(void);
void myfunc(void) { cout << "asda" << endl; }
void glFun(int a) { cout << a << endl; }
int main() {
func pfun = myfunc; /*赋值*/
pfun(); /*调用*/
pFun = glFun;
(*pFun)(2);
}