C++对C语言有很多功能上的扩展呐, 大部分扩展呢都是为了实现面向对象的这种机制。 那也有一些扩展呢,它跟面向对象的关系不太大,而且这些扩展呢也比较简单。所以我们可以先学一点这部分的内容。 那我们第一个要讲的概念呢是引用。 下面的这个写法它就定义了一个引用,类型名&引用名等于某个变量名, 这条语句定义了一个引用,并且把它初始化成引用某个变量, 具体的例子在这。int & r=n, 这个时候呢,r就是一个引用,它引用了n,r的类型是什么呢? 是int &,注意是int &, r引用了n,那到底是什么意思呢? 啊,我们说某一个变量的引用它就等价于这个变量, 它相当于这个变量的一个别名, 也就是说,如果我引用了你,那我跟你就是一回事了。那我打自己一耳光,你会觉得脸上疼。 你要是吃到了好吃的东西,我也会觉得很香。是等价的。 看这个例子。我们让r引用了n, 然后我们对r进行赋值。然后我们把r的值给它输出, 那毫无悬念,当然就是4对吧,那么输出n的值,这个时候应该是多少呢? 注意,我们已经说了,r引用了n,r跟n就是一回事了,那我们前面对r进行了赋值, 也会改变n的值。所以这条语句输出的结果就是4. 接下来我们对n进行了赋值,然后再输出r, 那么r数出来应该是多少啊? 很显然,n跟r是一回事,n改了,r也该了,所以输出结果就是5. 那我们使用引用的时候,有三条需要注意的地方, 第一点,我们定义引用的时候,一定要将其初始化成引用某个变量。 啊,你只能让它初始化成引用某一个变量,它不能引用别的一些东西。 第二条,就这个引用一旦初始化以后啊,它就一直引用 那个初始化的变量,它不会再引用别的变量了。也就是说这个引用它是从一而终的。 要注意了。第三条,就是引用, 它只能引用变量,不能引用常量和表达式。 我们再看下面一个例子。 啊,在这个例子里面,我们让r1引用了a, 然后呢,r2又被初始化成r1, 那么实际上这个时候r2它也引用了这个a, 然后我们对r2进行赋值,这个时候我们会影响到谁呢? 我知道了,r2和r1都引用了a,那你对r2进行赋值, 当然也就会影响到a,所以我们输出a的时候结果是多少呢? 是10。然后,我们再将r1等于b, 这个时候是否r1就变成了引用了b呢? 不是,对吧,前面说了。引用是从一而终的。 那r1一开始它引用了a,它后面就一直引用a了。 你让r1等于b,它的含义并不是r1去引用了b, 而是用b对r1进行赋值。 也就是说,r1以及r1所引用的东西, 它的值都变成跟b相等了。 那r1引用了谁啊?r1引用了a,所以我们在下面输出a的时候,我们会发现a的值跟b是相等的。 b是5对吧?所以输出的值就是5. 这既是引用的概念。 下面我们看一个比较有说服力的例子,能够说明引用是比较好用的。 我们考虑在C语言里面,我们如何编写交换两个整形变量的值的函数呢? 我们能否这样编写呢?啊,swap( int a, int b),啊, 然后在这里面交换a和b的值,接下来我们有两个n1和n2变量的值需要交换,我们调用swap(n1, n2) 这时候大家,我想大家是知道的,n1和n2的值并不会因为调用了这条函数而被改变,对吧? 为什么呢?因为我们 C语言里面学过,a,b是形参,n1,n2是实参, 在这个函数里面,形参的值修改了,它不会影响到实参n1,n2对吧。 那好了如果在C语言里面我们就是要写一个swap函数它能交换n1,n2的值,该怎么办呢?回忆一下。 我们写swap,然后两个参数是指针,啊, 然后在这里面我们对这里两个指针所指向的内容进行了交换, 然后我们调用swap的时候,哎,这个参数不再是n1和n2了,而是& n1和& n2 也就是说,这时候参数变成了n1和n2的地址。 在这种情况下,我们知道,这个swap函数执行完以后n1和n2的值确实是会被修改的。 但这一一个函数以及它调用的方式看上去都比较丑陋啦, 又多了这两个运算符,然后里面一大堆的星号,看上去很丑。 那有了C++的引用的概念以后, 我们新的swap函数写起来就可以看上去体面的多了, 啊,这时候我们用两个引用作为参数, 作为swap参数,然后呢在这里面交换两个参数a,b的值, 而且我们调用swap的时候,调用swap函数的时候,直接就用n1和n2作为参数就可以了。 用不着取地址的运算符了。那么这个swap函数执行完以后, 我们发现,n1和n2的值它确实会被修改。 为什么可以达到这样的效果呢?这就是引用的一个魔力了。 我们看到,进到ii, 它就引用了n1,是吧?b呢,是不是就引用了n2, 那根据我们对引用概念的解释,一个ii的东西是一回事,是等价的。 那么也就是说,在这个swap函数里面,a它就是等价于n1, b它也是等价于n2, 也就是说在这个函数里面我们修改了a的值,修改了b的值, 直接也会导致这个n1和n2的值被修改。 所以说执行完这个swap函数调用语句以后, n1和n2的值就被交换了。 再看一种引用的比较,看上去比较炫的这个用法,啊,就是引用可以作为函数的返回值, 这里有个全局变量,这里有一个看上去比较奇怪的函数,SetValue它的返回值 是一个整型的引用。然后它返回的值呢又是n, 那这时候我就问问大家,这个函数的返回值是一个引用,它引用了谁呢? 啊,你在这里写了return n,那很显然,这个函数的返回值就引用了n,那一个函数的返回值是引用, 能够带来什么好处呢? 直观的说,我们就可以把这个函数调用写在赋值号的左边,啊,我们对一个函数调用返回的结果可以进行赋值, 看上去很神奇,对吧?呃, 这时候,这条语句产生的效果是什么呢?由于 SetValue它的返回值是一个引用,而这个引用又引用了全局变量n, 那么这个时候,我们对SetValue的返回值进行赋值,实际上就是等价于对n进行赋值。 所以我们输出n的时候我们看到,n的值变成了40。 那这个函数返回值把它写成引用, 在这个例子里面并不能很充分的说明它到底有什么作用,对吧? 你为什么要把一个函数调用表达式写在赋值号的左边呢?有什么意义呢? 这个大家可以先不明觉厉,啊,等到学到后面了,大家学会了,就会喜大普奔了。 我们以后再讲。那引用还有一种用法,就是常引用。 也就是说我们在定义引用的时候,前面可以加const的关键字, 这个时候这个引用就变成了常引用。比方说这里一个r,前面加了const的关键字, 这个r就是一个常引用。那r的类型是什么呢?r的类型就是const in & 哎对了。那常引用有什么特点啊? 常引用的特点,不能通过常引用去修改其引用的内容。 比方说,这个r是一个常引用,它引用了一个n,对吧? 那我们让r等于200,这条赋值语句,哎,会编译出错, 为什么呀?因为r是一个常引用,而你这一条语句是试图通过r去修改 它所引用的东西,也就是n的值,对吧?所以编译就会出错。 至于n=300,这个就没有问题, 因为我们只是说了,不能通过常引用去修改其引用的内容, 并不是说,它引用的内容就绝对不能被修改。 可以用其他方式被修改的。我们还要注意这个常引用和非常引用的转换问题。 就是我们假设T是一个一种类型的名字,那么const T &和T &是不同的这个类型。 那T &类型的引用或者T类型的变量可以用来初始化 const T &类型的引用,那反过来,const T &类型的常变量,啊,常变量的概念以后再说了, 以及这个const T &类型的引用,我们就不能够用来初始化 T &类型的引用,除非我们进行一下强制类型转换。大家在这里先记住这一条就可以了。 那下一小节的内容就是const的用法。就是我们刚才提到的const。