读完这一章,最大的收获就是之前对string和字符串(文中称为c风格字符串)的混淆,那个时候很懒的,在string和char*之间相互赋值转换时,如果成功了就成功了,不成功就都换成char*,也不知道是怎么回事,也不去研究是怎么回事。另外,好像我突然明白了多维数组的分析,之前看C专家编程的时候感觉记不下来,其实就是我没有很理解。
第4章的篇头的这段话我觉得很有意味,“C++语言提供了两种类似于vector和迭代器类型的低级复合类型 -- 数组和指针。与vector类型相似,数组也可以保存某种类型的一组对象;而它们的区别在于,数组的长度是固定的。数组一经创建,就不允许添加新的元素。指针则可以像迭代器一样用于遍历和检查数组中的元素。”
1. 指针
指针保存的是另一对象的地址。一个有效的指针必须是以下三种状态这一:(1)保存一个特定的地址;(2)指向某个对象后面的另一个对象;(3)0值(即null),不指向任何对象。对于第二种怦,我不是很理解,在后面的讲解中,我猜测,是不是指的像一个指向数组的指针,加了一个值后,变成了指向“某个对象后面的另一对象”了?
另外还有一个概念上的澄清,设int a[10] = {0}, int* p = a, 当我们使用p时,使用的是p中保存的地址,p是一个指针;当我们使用*p时,叫做p的解引用(dereference),使用(也包括修改)的是p中保存的地址中的数据。以前看到“解引用”这样的名词,我都觉得有些惴惴不安,因为我不确定是什么意思,呵呵。
2. 指针和引用(&)的区别
虽然引用和指针都可间接访问另一个值,但是它们之间有两个重要区别。(1)引用总是指向某个对象:定义引用时没有初始化是错误的。(2)赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向同一个特定对象。例如:
int ival = 1024, ival2 = 2048;
int* p1 = &ival, *p2 = &ival2;
//赋值结束后,p1所指向的ival对象值不变,赋值操作修改了p1指针的值
//使其指向另一个不同的对象
p1 = p2;
//下面的赋值操作修改了r1引用的值ival对象,而非引用本身。
//赋值结束后,这两个引用还是分别指向原来关联的对象,此时这两个对象的值相等
int &r1 = ival, &r2 = ival2;
r1 = r2;
3. 指针和const限定符
指针和const限定符之间的有两种交互类型:指向const对象的指针和const指针。
我们经常使用指针来修改其所指对象的值。但是,如果指针指向const对象,则不允许用指针改变其所指的const值。为了保证这个特性,c++语言强制要求指向const对象的指针也必须具有const特性。(这与之前的引用一样)
允许把非const对象的地址赋值给指向const对象的指针,这时不能通过该指针去修改对象的值,但可以通过其他指向该对象的指针去修改该对象。可以把指向const的指针理解为“自以为指向const的指针”。
const指针指的是指针本身的值不能改变,也就是它将一直指向一个特定的对象,该对象如果不是const的则该对象是可以被修改的。(突然想到,这个指针应该可以释放吧?释放后这个指针会变成怎么样的呢?-- 尝试了一下,可以释放,但是这个指针再也不能被赋给其他人了)
关于它们的书写方式,也是挺容易让人混淆的,
const double pi = 3.14;
double *ptr = π //error: ptr is a plain pointer
const double* ptr = π //ok: ptr is a pointer to const
int errNumb = 0;
int *const curErr = &errNumb; //curErr is a const pointer
curErr = curErr; //error: curErr is a const pointer, it can't be assigned again
4. 指针和typedef
在typedef中使用指针往往会带来意外的结果。下面是一个几乎所有人刚开始时都会答错的问题(确实):
typedef string *pstring;
const pstring cstr;
请问cstr是什么类型的?(我把pstring换成string*,然后分析得出,cstr是一个指向string类型的const对象的指针。结果是错的。)
错误的原因在于将typedef当做文本扩展了。声明string pstring时,const修饰的是pstring类型,这是一个指针,因此,该声明应该是把cstr定义为指向string类型对象的const指针。这个定义等价于:
string *const cstr;
阅读const声明语句产生的部分问题,源于const限定符既可以放在类型前面也可以放在类型后,
string const s1; 和 const string s1是一样的。
上面的语句,如果写成pstring const cstr,做文本扩展的时候就变成string* const cstr了。所以最重要的,还是得把pstring看成一个整体。
5. 动态数组
“数组类型的变量有三个重要的限制:数组长度固定不变,在编译时必须知道其长度,数组只在定义它的块语句内存在。”为什么要摘录这句话呢?因为它提醒了我数组在程序内存中是在什么地方(数组应该是在静态存储区或栈上),当数组不是全局变量时,比如是在函数里,离开该函数后数组就不在了。而动态数组则不一样,它是分配在堆上的,动态分配的数组将一直存在,直到程序显式地释放它。
另外,释放数组的时候应该是
int* pia = new int[10];
delete [] pia;
关键字delete和指针之间的空方括号是必不可少的:它告诉编译器该指针指向的是自由存储区中的数组,而并非单个对象。
6. 混合使用标准库类string和C风格字符串
由于C风格字符串与字符串字面值具有相同的数据类型,而且都是以null结束,因此可以把c风格字符串用在任何可以使用字符串字面值的地方:
(1)可以使用C风格字符串对string对象进行初始化或赋值
(2)string类型的加法操作需要两个操作数,可以使用c风格字符串作为其中的一个操作数。
但是反之则不成立:在要求c风格字符串的地方不可以直接使用标准库string类型对象。但是string类提供了一个名为c_str() 的成员函数实现我们的要求:
const char* str = st2.c_str(); //查看c_str()的声明,可以发现其返回的是一个指向const char的指针
7. 指针和多维数组
定义指向数组的指针与如何定义数组本身类似:首先声明元素类型,后接(数组)变量名字和维数。窍门在于(数组)变量的名字其实是指针,因此需要在标识符前加上*。
int *ip[4]; //包含4个指向int的指针数组
int (*ip) [4]; //指向包含有4个int元素的数组的指针
下面的图是从C专家编程里借过来的:
可以用下面任何一种方法为int apricot[2][3][5]在内存中定位:
http://philoscience.iteye.com/category/176811