1.引用和指针
实际是一个变量的别名,类型 &来声明
与指针的不同:
1、并且不能为NULL,且一定要初始化,不能改变;指针可以为NULL,且可以指向不同的对象
2、不能有多级引用;指针可以有多级
3、++ 和 -- 操作,指针是对地址,引用是对引用的变量操作;引用访问时直接访问,而指针访问时间接访问
code:
#include<iostream>
using namespace std;
void swap1(int &a, int &b){
int t = a;
a = b;
b = t;
}
void swap2(int* a, int* b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int x=1, y=2;
int* p1=&x,* p2=&y;
cout<<x<<y<<endl;
swap1(x,y);
cout<<x<<y<<endl;
swap2(p1,p2);
cout<<x<<y<<endl;
return 0;
}
结果:12 21 12
可见都实现了交换
引用和指针的意义用一张自己做的图帮助理解,但是内存中显然不是这样的。
引用就像变量名一样绑在content后面,使得该引用不能再指向别的content,当然一个变量可以有很多个引用;指针则是另外的变量,存储了content所在的内存地址,
在swap函数中,指针是通过&地址的方式间接改变content的值;引用则是直接改变。
2.指向常量的指针
就是一种只读的指针,能通过指针访问指向的值,但不能通过指针改变指向的值;并且可以改变指针指向的地址。
#include<iostream>
using namespace std;
int main(){
int a=1,b=2;
const int *p = &a;
cout<<*p;
//*p = 3;试图改变a的值,报错
p = &b;//可以改变指针的指向
return 0;
}
3.指针数组
定义:int *p[3],表示的是一个有三个int类型指针的数组(本质是一个数组),其中p[1],p[2],p[0],都是指针,p是数组名
#include <iostream>
using namespace std;
int main(){
int line1[]={1,0,0};
int line2[]={0,1,0};
int line3[]={0,0,1,1};
int *pLine[3] = {line1,line2,line3};
cout<<"matrix:"<<endl;
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
cout<<pLine[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
结果:可以看到,指针数组可以当作二维数组去使用。因为指针数组中存放的是地址,但是比二维数组更为灵活(实现方式更像链表),可以每个长度不一样。
matrix:
1 0 0
0 1 0
0 0 1
PS:区别与数组指针,数组指针是一个指向数组的指针变量。如:int (*p)[2] 表示的是:一个指向含有两个int的数组的指针,对p指针的+1操作也是一个数组为单位。
#include <iostream>
using namespace std;
int main(){
int a[2][2]= {1,2,3,4};
int (*p)[2]=a;
cout<<*(*p+1)<<' '<<**(p+1);
return 0;
}
输出为: 2 3
相信应该理解了数组指针了。
4.指针类型函数
即返回值为一个地址的函数
注意:不要把非静态局部变量的地址返回,因为在函数结束的时候,非静态地址会被释放
#include <iostream>
using namespace std;
int* func(){
int local=0;//非静态局部变量在函数结束时,已被释放,返回的地址已经不是原来的local值
return &local;
}
int main(){
int* p;
p = func();//如果对p指针再赋值的话,将是非常危险的操作
return 0;
}
正确1:
#include <iostream>
using namespace std;
int* func(int* a, int num){
for(int i=0; i<num; i++){
if(a[i]==0)
return &a[i];//在数组内部找,返回的地址任然是在main函数中有意义的地址,而不是野指针
}
}
int main(){
int arr[10]={1,2,3,4,5,6,7,8,9,0};
int *p = func(arr, 10);//数组arr地址传给func
cout<<*p;
return 0;
}
正确2:用new分配内存空间,用delete释放内存
#include <iostream>
using namespace std;
int* func(){
return new int();//new一个int类型的内存地址
}
int main(){
int *p = func();
*p = 5;
cout<<*p;
delete p; //释放new出的内存地址
return 0;
}
5.指向函数的指针(函数指针)
实现函数回调,多个不同功能的函数的接口一致(但参数类型、个数和返回类型要一样)。
#include <iostream>
using namespace std;
//告诉compute函数,这有个func函数的地址可以使用,用*func可以找到,且func的参数一定是int,int,
//返回值也是int。
int compute(int a,int b,int(*func)(int, int)){
return func(a, b);//new一个int类型的地址
}
int max1(int a,int b){
return (a>b)?a:b;
}
int min1(int a,int b){
return (a<b)?a:b;
}
int sum1(int a,int b){
return a+b;
}
int main(){
int res,a=2,b=4;
res = compute(a,b,&max1);//在compute调用时,对函数名取地址,告诉程序这个func函数地址在哪
cout<<"max:"<<res<<endl;
res = compute(a,b,sum1);//函数名和&函数名一样都是表示函数的地址
cout<<"sum:"<<res<<endl;
return 0;
}
要与函数重载区分开(实现的是相同功能的同一接口,如都是求和的int和float之分)
有关函数重载的在我的另一个笔记里:https://blog.csdn.net/Wzz_Liu/article/details/81808893
6.this指针
实际上,我们新建一个对象,这个对象调用成员函数时,编译器自动将该对象的地址赋给this指针,成员函数访问成员变量,默认使用了this,this指针就是当前对象所在的地址。
比如类中的一个成员函数return x;,相当于return this->x;
7.对象指针(前向引用所不能解决的问题)
解决之前前向引用所不能解决的,类中的需要实例化另一个未先定义的类的对象
链接:https://blog.csdn.net/Wzz_Liu/article/details/82220552,即第五行B b那一句。
#include<iostream>
using namespace std;
class B; //前向引用声明
class A{
B *b;
public:
void f(B b);
};
class B{
public:
void g(A a);
};
int main(){
return 0;
}
编译器无需知道给定多少的空间给一个B类对象,只需要定义一个B类的指针b(大小为一个地址)