#include
int main()
{
int i = 10; // A simple integer variable
int &j = i; // A Reference to the variable i
j++; // Incrementing j will increment both i and j.
// check by printing values of i and j
cout<
// Now try to print the address of both variables i and j
cout<
// surprisingly both print the same address and make us feel that they are
// alias to the same memory location.
// In example below we will see what is the reality
return 0;
}
引用其实就是 c++ 中的指针常量。表达式 int &i = j; 将会被编译器转化成 int *const i = &j; 而引用之所以要初始化是因为 const 类型变量必须初始化,这个指针也必须有所指。下面我们再次聚焦到上面这段代码,并使用编译器的那套语法将引用替换掉。
[cpp] view plain copy print?
#include
int main()
{
int i = 10; // A simple integer variable
int *const j = &i; // A Reference to the variable i
(*j)++; // Incrementing j. Since reference variables are
// automatically dereferenced by compiler
// check by printing values of i and j
cout<
// A * is appended before j because it used to be reference variable
// and it should get automatically dereferenced.
return 0;
}
读者一定很奇怪为什么我上面这段代码会跳过打印地址这步。这里需要一些解释。因为引用变量时(使用变量时)会被编译器自动解引用的,那么一个诸如 cout << &j << endl; 的语句,编译器就会将其转化成语句 cout << &*j << endl; 现在 &* 会相互抵消,这句话变的毫无意义,而 cout 打印的 j 值就是 i 的地址,因为其定义语句为 int *const j = &i;
所以语句 cout << &i << &j << endl; 变成了 cout << &i << &*j << endl; 这两种情况都是打印输出 i 的地址。这就是当我们打印普通变量和引用变量的时候会输出相同地址的原因。
下面给出一段复杂一些的代码,来看看引用在级联 (cascading) 中是如何运作的。
[cpp] view plain copy print?
#include
int main()
{
int i = 10; // A Simple Integer variable
int &j = i; // A Reference to the variable
// Now we can also create a reference to reference variable.
int &k = j; // A reference to a reference variable
// Similarly we can also create another reference to the reference variable k
int &l = k; // A reference to a reference to a reference variable.
// Now if we increment any one of them the effect will be visible on all the
// variables.
// First print original values
// The print should be 10,10,10,10
cout<
// increment variable j
j++;
// The print should be 11,11,11,11
cout<
// increment variable k
k++;
// The print should be 12,12,12,12
cout<
// increment variable l
l++;
// The print should be 13,13,13,13
cout<
return 0;
}
下面这段代码是将上面代码中的引用替换之后代码,也就是说明我们不依赖编译器的自动替换功能,手动进行替换也能达到相同的目标。
[cpp] view plain copy print?
#include
int main()
{
int i = 10; // A Simple Integer variable
int *const j = &i; // A Reference to the variable
// The variable j will hold the address of i
// Now we can also create a reference to reference variable.
int *const k = &*j; // A reference to a reference variable
// The variable k will also hold the address of i because j
// is a reference variable and
// it gets auto dereferenced. After & and * cancels each other
// k will hold the value of
// j which it nothing but address of i
// Similarly we can also create another reference to the reference variable k
int *const l = &*k; // A reference to a reference to a reference variable.
// The variable l will also hold address of i because k holds address of i after
// & and * cancels each other.
// so we have seen that all the reference variable will actually holds the same
// variable address.
// Now if we increment any one of them the effect will be visible on all the
// variables.
// First print original values. The reference variables will have * prefixed because
// these variables gets automatically dereferenced.
// The print should be 10,10,10,10
cout<
// increment variable j
(*j)++;
// The print should be 11,11,11,11
cout<
// increment variable k
(*k)++;
// The print should be 12,12,12,12
cout<
// increment variable l
(*l)++;
// The print should be 13,13,13,13
cout <
return 0;
}
我们通过下面代码可以证明 c++ 的引用不是神马别名,它也会占用内存空间的。
[cpp] view plain copy print?
#include
class Test
{
int &i; // int *const i;
int &j; // int *const j;
int &k; // int *const k;
};
int main()
{
// This will print 12 i.e. size of 3 pointers
cout<
return 0;
}
结论
我希望这篇文章能把 c++ 引用的所有东东都解释清楚,然而我要指出的是 c++ 标准并没有解释编译器如何实现引用的行为。所以实现取决于编译器,而大多数情况下就是将其实现为一个 const 指针。
引用支持 c++ 虚函数机制的代码
[cpp] view plain copy print?
#include
class A
{
public:
virtual void print() { cout<
};
class B : public A
{
public:
virtual void print() { cout<
};
class C : public B
{
public:
virtual void print() { cout<
};
int main()
{
C c1;
A &a1 = c1;
a1.print(); // prints C
A a2 = c1;
a2.print(); // prints A
return 0;
}
上述代码使用引用支持虚函数机制。如果引用仅仅是一个别名,那如何实现虚函数机制,而虚函数机制所需要的动态信息只能通过指针才能实现,所以更加说明引用其实就是一个 const 指针。
补充:const 指针(指针常量)与指向const的指针(常量指针)
当使用带有const的指针时其实有两种意思。一种指的是你不能修改指针本身的内容,另一种指的是你不能修改指针指向的内容。听起来有点混淆一会放个例子上来就明白了。
先说指向const的指针,它的意思是指针指向的内容是不能被修改的。它有两种写法。
const int* p; (推荐)
int const* p;
第一种可以理解为,p是一个指针,它指向的内容是const int 类型。p本身不用初始化它可以指向任何标示符,但它指向的内容是不能被改变的。
第二种很容易被理解成是p是一个指向int的const指针(指针本身不能被修改),但这样理解是错误的,它也是表示的是指向const的指针(指针指向的内容是不能被修改的),它跟第一种表达的是一个意思。为了避免混淆推荐大家用第一种。
再说const指针,它的意思是指针本身的值是不能被修改的。它只有一种写法
int* const p=一个地址; (因为指针本身的值是不能被修改的所以它必须被初始化)
这种形式可以被理解为,p是一个指针,这个指针是指向int 的const指针。它指向的值是可以被改变的如*p=3;
还有一种情况是这个指针本身和它指向的内容都是不能被改变的,请往下看。
const int* const p=一个地址;
int const* const p=一个地址;
看了上面的内容是不是有点晕,没关系,你不用去背它,用的多了就知道了,还有个技巧,通过上面的观察我们不难总结出一点规律,是什么呢?也许你已经看出来了,什么!竟然没看也来,那只好还得听我唠叨了。这个规律就是: 指向const的指针(指针指向的内容不能被修改)const关健字总是出现在*的左边而const指针(指针本身不能被修改)const关健字总是出现在*的右边,那不用说两个const中间加个*肯定是指针本身和它指向的内容都是不能被改变的。有了这个规则是不是就好记多了。
什么还是晕,那就看下面的程序,你把它编译一下看看错误提示就明白了。
1 #include
2
3 using namespace std;
4
5 int main(int argc, char *argv[])
6 {
7 int a=3;
8 int b;
9
10 /*定义指向const的指针(指针指向的内容不能被修改)*/
11 const int* p1;
12 int const* p2;
13
14 /*定义const指针(由于指针本身的值不能改变所以必须得初始化)*/
15 int* const p3=&a;
16
17 /*指针本身和它指向的内容都是不能被改变的所以也得初始化*/
18 const int* const p4=&a;
19 int const* const p5=&b;
20
21 p1=p2=&a; //正确
22 *p1=*p2=8; //不正确(指针指向的内容不能被修改)
23
24 *p3=5; //正确
25 p3=p1; //不正确(指针本身的值不能改变)
26
27 p4=p5;//不正确 (指针本身和它指向的内容都是不能被改变)
28 *p4=*p5=4; //不正确(指针本身和它指向的内容都是不能被改变)
29
30 return 0;
31 }