关于指针和引用一直是学习C++的同学们争论的焦点,什么时候用指针,什么时候用引用,还有怎么引用数组,这么用指针访问数组,以及初始化的问题。
不过有一些文章我在很早就已经写过,但是由于当时时间不充分,自己也都是随性写的,可以参看以前我的一个文章:http://blog.csdn.net/pbymw8iwm/article/details/8555197
这里就详细说一下:
1.关于引用和指针的初始化
首先没有空引用,一个引用必须代表某个对象,如果你有一个变量,目的用来指向领一个对象,但是它有可能不指向任何对象。
程序为指针分配内存空间,但是引用不需要分配内存空间。
比如这样的例子,
int a = 0;
int* p = NULL;
int& pref= *p;
p = &a;
上面的这段代码,相信你心里有数了,这个例子告诉我们指针可以被重复赋值,执行另外一个对象,但是引用却总是执行他最初获得的那个对象,所以第三行的代码你将会得到一个无效的引用,即使后来p已经指向了a的地址。
到这里就不得不再说一下指针和引用去访问数组了,并且分别用他们初始化数组的方式了:
int main() { int a[10]={1,2,3,4,5,6,7,8,9,0}; int (*pa)[10] = &a; printf("%d",(*pa)[8]); }
这个是对指针pa的初始化和通过指针对数组的访问,首先pa是一个指针,你看到pa前面的*,就该知道,他的确是个指针,那么它指向的是什么 int [10] ,也就是说有10个整形元素的数组,怎么初始化呢,我们都知道数组名代表的是数组的首地址,那么我们应该用数组a的首地址去初始化它。
int main() { int a[10]={1,2,3,4,5,6,7,8,9,0}; int (&pa)[10] = a; printf("%d",(pa)[8]); }
这个是对引用pa的初始化以及通过引用对数组访问,首先pa是一个引用,你看到pa前面的&,你就知道他的确是引用,他引用的是一个 int 【10】的数组,引用这个数组的名字就代表对这个数组的引用,也就是说此时pa和a是一样的,那么对于数组的访问,怎么用引用访问,相信你知道了。
2 关于指针和引用的函数传参
接着就要说传参数了,
class A { public: A(){cout<<"构造函数"<<endl;} ~A(){cout<<"析构函数"<<endl;} A(const A &a){cout<<"拷贝构造函数"<<endl;}; A& operator=(const A &a) { cout<<"赋值操作符"<<endl; return *this; } }; A show(A a) { return a; } A showRef(A& a) { return a; } A showPoint(A* a) { return *a; } int main() { A a; show(a); /* 拷贝构造函数 拷贝构造函数 析构函数 析构函数 */ showRef(a); /* 拷贝构造函数 析构函数 */ showPoint(&a); /* 拷贝构造函数 析构函数 */ }
说明了传参数的时候引用和指针的效果相似。都不需要在这个栈上创建新的对象,而普通的传参则会调用拷贝构造函数,用主调函数的参数对象来初始化北调函数的对象。
3 练习
int main() { const int& a = 0; const int* p = 0; cout << a << *p; }
这个程序有什么问题么?(a是一个引用,引用的是一个常量,因此a的值是0,p是一个指针,指向的是一个空指针,因此对p的访问是非法的)
再看一个如下程序,看看这个段代码有什么输出?
int main() { int a = 0; const int& const ref = a; const int* const p = &a; a = 1; cout << a << endl; cout << *p; }
结果显示a和*p都是1,为什么呢,ref是一个常引用,引用的是一个const常量,其实ref本身就是const引用,因为他一旦引用到他初始化的那个变量后就不会再改变了,此时a但是是非const的,所以a的改变也会影响到ref,即使ref指向的是const常量,但由于a本身是非const的,同样的p也是一样的。