- 5.1 引用
- 引用可以看做是对象的别名,可以通过引用来操作对象,要注意,引用在声明时要初始化。
int a=10;
int&m=a;
int*p=&a;
int*&pa=p;
例子1:
char*p="abc";
char*q="def";
swap(p,q);
cout<<p<<","<<q<<endl;
输出:abc,def
因为,指针传入函数,函数内部会临时分配临时变量用于存储传进来的值,在函数返回后,临时变量销毁,不会对指针有任何操作。
若是要解决这个问题,则需要传递指针引用。
voidswap(char **,char**);
swap(&p,&q);
即可。
例子2:
float f=5;
floatf1()
{
return f;
}
float&f2()
{
return f;
}
voidmain()
{
float c=f1();
float &d=f2();
float e=f2();
float &h=f1();//error
}
其中,main 函数中第二行第三行效果相同,但是第四行会出现错误。提示为:非常量引用的初始值必须为左值。
这是因为 ,在函数内部,为全局变量f,会自动赋给临时变量,这就相当于h是对临时变量temp的引用,而临时变量用后会被销毁。
还有一种引用是常量引用,形如 constint &p=a;在这种情况下,不允许通过引用p修改变量a 的值,达到安全的目的。常量引用的引用对象可以是变量,
但是如果引用对象是常量,那么引用一定要为常量引用,即:
constint a=5;
int&p=a;//error
constint &p=a;//correct
例子3:
指针与引用的区别:
1、初始化要求不同。 引用在创建的同时必须初始化,而指针在创建的同时,可以不必初始化。
2、可修改性不同。 引用一旦确定,就不能再更改,而指针可以重新指向其他对象。
3、不存在NULL的引用。引用必须指向明确的对象,而指针则可以为空,也可以指向任意对象。
4、测试的区别。 引用不为空,所以测试是不需要验证其有效性,但是指针则不一定,所以需要验证其合法性。
5、应用的区别。 引用适用于指向对象后不需要更改的场景中,而指针适用于需要变换指定对象情况。
补充:指针由于存在隐患,所以安全性不如引用。
5.2 指针基础
例子4:
指针的定义声明:
int*p; 定义一个整型指针
int **p; 定义一个指向指针的指针
int*p[10]; 定义一个有十个指针的指针数组
int(*p)[10]; 定义一个有十个整数的数组指针
int*p(int a); 定义一个形参为整型的函数指针
int*p[10](int a);定义一个元素为形参是整型的函数的数组指针
例子5:
指针的比较:
void main()
{
char str1[]="abc";
char str2[]="abc";
const char str3[]="abc";
const char str4[]="abc";
const char *str5="abc";
const char *str6="abc";
char *str7="abc";
char *str8="abc";
cout<<(str1==str2)<<endl;
cout<<(str3==str4)<<endl;
cout<<(str6==str7)<<endl;
cout<<(str5==str6)<<endl;
cout<<(str7==str8)<<endl;
}
输出为:0 0 1 1 1
这是因为“abc”是在栈中分配,是以\0结尾的字符串,所以str1和str2是不同的地址,对于str3和str4,const只是说明不可更改,不改变存储位置和方式,所以不同;
而指针str5,6,7,也在栈中分配,都指向“abc”,但是“abc”是放在数据区中的,是同一块区域。
对空指针进行内存读取会产生错误。如int*p=0;int a=*p;
注意 常量指针和 指针常量的区别: 常量指针是指向常量的指针,指针常量是类型为指针的常量。
5.2 this指针
在C++中,对象的this指针并不是对象的比部分,不会影响sizeof的结果。this 指针的作用域是累的内部,在累的静态成员函数中访问累的非静态成员时,编译器会自动将对象地址作为隐含参数传递给函数。
也就是说,没有this指针,编译器也会在运行的时候,为对象分配一个this指针。他作为非静态成员函数的隐含参数,对各个成员均可以通过this访问。
例如 : date.setMonth(9);在this指针的帮助下,可以转换为:
setMonth(&date,9);
this 指针的使用情况如下:
一种是在类的非静态成员函数中返回类对象本身的时候,直接使用return *this;二是当参数名和成员变量名相同时,用来区分二者,如this->n=n;
注意:this指针是存在于属于类的非静态成员函数中,而静态成员函数不属于类,所以不存在this指针。
例子6:
指针数组 与数组指针:
void main()
{
char*str[]={"welcome","to","Fortemedia","Nanjing"};
char **p=str+1; //1
str[0]=(*p++)+2; //2
str[1]=*(p+1); //3
str[2]=p[1]+3; //4
str[3]=p[0]+(str[2]-str[1]);//5
cout<<str[0]<<","<<str[1]<<","<<str[2]<<",'<<str[3]<<endl;
}
输出为:空,Nanjing,jing,g
1行后,p指向了str[1],即“to”;
2行后,*p是指向"to"的,+2后,就指向了“to”的末尾"\0",也就是为空,然后p自增,指向了Fortemedia字符串,也就是str[2];
3行后,p+1指向Nanjing,所以str[1]为"Nanjing";
4行后,先计算等号右边,p[1]是指向“Nanjing”的,再移动三个位置,就是"jing",所以str[2]="jing",由于p此时也是指向str[2]的,所以p的内容也发生了变化,这一步的操作相当于把str[2]的子串赋给了str[2];
5行后,由于此时p指向了"jing",str[2]-str[1]=3,所以p右移三个位置的值就是str[3]的值,也就是g;
5.4 函数指针和指针函数
指针函数本身是一个函数,只是他的返回类型是指针,定义形式为:type *name(parameter){};
函数指针本身是一个指针,只是其类型为函数类型
5.5 野指针
野指针有两种:
1、没有初始化,默认值是随机的。
2、被free或delete后没有置为NULL,即悬垂指针。
new/delete和 malloc/free:
对于非内部数据类型的对象而言,对象在小王仟要自动执行析构函数。由于malloc/free函数是库函数而不是运算符,不在编译器控制权限内,不能够把执行构造函数和析构函数的任务强加给malloc/free函数是库函数而不是运算符,不在编译器控制权限内,不能够把执行构造函数和析构函数的任务强加给malloc/free
只能使用new/delete函数。
例子6:
char* get()
{
char str[]="welcome";
return str;
}
void main()
{
char *p=get();
cout<<p<<endl;
}
输出 p可能会乱码!!!
void get(char *p)
{
p=(char *)malloc(100);
}
void main()
{
char *str=NULL;
get(str);
}
str 并不会指向分配的存储空间,依然为NULL,而对应内存没有指针可以指向,这就会导致内存泄露。
例子7:
内存的分配方式:
内存分配一共有三种,分别是静态存储区、栈、堆的内存分配;
1、静态存储区分配内存。编译器在程序编译阶段就已经将内存分配好,并且在程序运行的整个过程中都一直存在。
2、栈上的内存分配。在执行函数时,函数内局部变量的存储单元可以在栈上创建,函数结束时这些存储单元自动被释放。处理器的制定集中有关于栈内存分配的运算,因此效率很高,但是分配的内存容量有限。
3、在堆上分配内存。也成为内存的动态分配。程序在运行的时候用malloc或new运算符申请任意大小的内存,程序员要用free函数或delete运算符来释放内存。动态内存分配很灵活,但问题也很多。
5.6 指针和句柄的区别
windows环境中,句柄是用来表示i项目的,这项目包括:
模块(module),任务(task),实力(instance),文件(file),内存块(blockofmemory),菜单(menu),控制(control),字体(font),资源(resource),包括图标,光标,字符串等,GDI对象,包括位图,画刷,原文件,掉色斑,画壁,区域以及设备描述表。
windows是以虚拟内存为基础的操作系统。在这种操作系统下,windows内存管理器在内存中来移动对象,从而满足各种应用程序的内存需要。对象被移动意味着他的地址发生变化。由于地址变化,windows专门为各应用程序留出一些内存储地址,
专门用来等级个应用对象在内存中的地址变化,而这些地址(存储单元)的地址本身是不变的。windows内存管理器在内存中移动对象后,把对象的新地址告知句柄地址来保存。这样只需要知道句柄的地址就可以间接的知道对象在内存中的地址,
这个地址在对象装在的时候由系统来分配,当系统卸载是有释放给系统。
因此,windows程序并不是通过物理地址来表示内存块、文件、任务或动态装入模块的,相反,windowsAPI给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。
在windows变成中会大量使用句柄,HINSTANCE,HBITMAP,HDC(设备描述句柄),HICON图标句柄,还有一个通用句柄handle。c
程序执行的顺序是:
句柄地址(稳定)-->记载对象在内存中的地址-->对象在内存中的地址(不稳定)-->实际对象
每次程序重新启动后, 分配的句柄,并不一定是原来的句柄,很多情况下是不一样的。
不同之处:
1、句柄是一个复杂的结构,并且可以与系统有关,例如线程的句柄,他可以指向一个类或者结构,并且和系统有很密切的关系,当一个线程由于不可预料的原因二终止的时候,系统就可以返回他所占用的资料,如CPU,内存等
反之,句柄的某些项是与系统进行交互的。
2、指针也可以指向一个复杂的结构,但是通常是由晕乎定义的,所以必要的工作要由用户来完成,特别是删除部分。
第五章结束