第2课 - C++中的引用
1. C++中的布尔类型
C++在C语言的基本类型系统之上增加了bool。C++中的bool可以取得值只有true和false,true代表真值,编译器内部用1来表示;false代表非真值,编译器内部用0来表示。理论上的bool只占用一个字节。
如果多个bool变量定义在一起,可能会各占一个bit,这取决于编译器的实现。
#include <stdio.h
int main(int argc, char *argv[])
{
int a;
bool b = true;
printf("b = %d, sizeof(b) = %d ", b, sizeof(b));
b = 3;
a = b;
printf("a = %d, b = %d ", a, b);
b = -5;
a = b;
printf("a = %d, b = %d ", a, b);
a = 10;
b = a;
printf("a = %d, b = %d ", a, b);
b = 0;
printf("b = %d ", b);
printf("Press enter to continue ...");
getchar();
return 0;
}
b = 1, sizeof(b) = 1
a = 1, b = 1
a = 1, b = 1
a = 10, b = 1
b = 0
bool类型只有true(非0)和false(0)两个值。C++编译器会在赋值时将非0转化为ture,0转化为false
#include <stdio.h>
int main(int argc, char *argv[])
{
bool b = false;
printf("b = %d ", b);
b++;
printf("b = %d ", b);
b = b + 1;
printf("b = %d ", b);
printf("Press enter to continue ...");
getchar();
return 0;
}
运行结果:
b = 0
b = 1
b = 1
2. 三目运算符
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
(a < b ? a : b) = 3;
printf("a = %d, b = %d ", a, b);
printf("Press enter to continue ...");
getchar();
return 0;
}
运行结果:a = 3, b = 2
C语言中的三目运算符返回的是变量值,不能作为左值使用。
C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方。但是,三目运算符可能返回值中有一个是常量值,则不能作为左值使用。
3. C++中的引用
(1)变量名回顾
变量是一段实际连续存储空间的别名。
程序中通过变量来申请并重新命名存储空间。
通过变量的名字可以使用存储空间。
思考:对于一段连续的存储空间只能有一个别名吗?
(2)引用
引用可以看做一个已经定义变量的别名。
引用的语法:type& name = var;
tip:普通引用在声明时必须用其他的变量来进行初始化。
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 4;
int& b = a;
b = 5;
printf("a = %d ", a);
printf("b = %d ", b);
printf("&a = %08X ", &a);
printf("&b = %08X ", &b);
printf("Press enter to continue ...");
getchar();
return 0;
}
运行结果:
a = 5
b = 5
&a = 0028FF18
&b = 0028FF18
引用的意义:
引用作为其他变量的别名而存在,因此在一些场合可以代替指针。引用相对于指针来说具有更好的可读性和使用性。
我们看一下swap()函数的实现对比:
void swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
swap(a, b);
void swap(int* pa, int* pb)
{
int t = *pa;
*pa = *pb;
*pb =t;
}
swap(&a, &b);
tip:引用作为函数参数声明时不进行初始化。
(3)const引用
在C++中可以声明const引用。
const Type& name = var;
tip:const引用让变量拥有只读属性。
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 4;
const int& b = a;
int* p = (int*)&b;
//b = 5; 这段程序有错,因为const引用是只读的。
*p = 5;
printf("a = %d ", a);
printf("b = %d ", b);
printf("Press enter to continue ...");
getchar();
return 0;
}
a = 5
b = 5
(4)当使用常量对const引用进行初始化时,C++编译器会为常量分配空间,并将引用作为这段空间的别名。
#include <stdio.h>
int main(int argc, char *argv[])
{
const int& b = 1;
int* p = (int*)&b;
//b = 5;
*p = 5;
printf("b = %d ", b);
printf("Press enter to continue ...");
getchar();
return 0;
}
运行结果:b = 5
使用常量对const引用初始化后将生成一个只读变量。若const常量引用是一个常量,那么它的值是没办法通过指针来改变的。这里的只读变量和C语言中的const变量是只读变量是一样的。
(5)引用有自己的存储空间
#include <stdio.h>
struct TRef
{
int& a;
int& b;
};
int main(int argc, char *argv[])
{
printf("sizeof(TRef) = %d ", sizeof(TRef));
printf("Press enter to continue ...");
getchar();
return 0;
}
运行结果:sizeof(TRef) = 8
l 引用若是没有自己的存储空间,那么定义的结构体占的内存就会是0。
l 引用在C++内部的实现是一个常指针:Type& name ---Type* const name
C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占的空间大小与指针相同。
从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做的细节隐藏。
void f(int& a)
{ a = 5; }
等价于
void f(int * const a)
{ *a = 5; }
我们看一下下面的程序:
#include <stdio.h>
struct TRef
{
int& a;
int& b;
int& c;
};
int main(int argc, char *argv[])
{
int a = 1;
int b = 2;
int c = 3;
TRef rA = {a, b, c};
printf("&a = %08X ", &a);
printf("&b = %08X ", &b);
printf("&c = %08X ", &c);
printf("&rA = %08X ", &rA);
printf("sizeof(TRef) = %d ", sizeof(rA));
printf("Press enter to continue ...");
getchar();
return 0;
}
运行结果:
&a = 0028FF1C
&b = 0028FF18
&c = 0028FF14
&rA = 0028FF08
sizeof(TRef) = 12
我们看到rA的地址和a, b, c的地址是不同的,而且它还有自己的存储空间。rA和c的地址正好相差了12个字节。
(6)当喊得的返回值是引用的时候
若返回栈变量:不能成为其它引用的初始值,不能作为右值使用。
若返回静态变量或者全局变量:可以成为其他引用的初始值,既可以作为右值使用,也可以作为左值使用。
#include <stdio.h>
int& f()
{
static int a = 0;
return a;
}
int& g()
{
int a = 0;
return a;
}
int main()
{
int a = g();
int& b = g();
f() = 10;
printf("a = %d ", a);
printf("b = %d ", b);
printf("f() = %d ", f());
printf("Press enter to continue ..."); //若不加这两条代码,程序就会一闪而过。
getchar();
return 0;
}
运行结果:
a = 0
b = -2
f() = 10
a的值就是一个简单的函数调用,会将函数g中局部变量a的值给主函数中的a。b引用的是一个已经释放掉的局部变量的别名,相当于b就是函数g中局部变量a的别名,是没有意义的,会随机给值。f的返回值是一个静态局部变量,是可以作为左值使用的,所以可以给函数f赋值10。也就是相当于给它的静态变量赋值10。
(7)C++对三目运算符做了什么
当三目运算符的可能但是只都是变量的时候,返回的是变量的引用。
当三目运算符的可能返回值中有常量的时候,返回的是值。
int a = 1;
int b = 2;
(a < b ? a : b) = 3; //正确,返回a或b的引用,可以作为右值使用。
(a < b ?1 : b) = 3; //错误,可能返回中有常量,返回的是值,不能作为右值使用。
小结:
(1) bool类型是C++新增加的基础类型,它的值只能是true和false。
(2) C++中的引用可以看做变量的别名来使用。
(3) C++中的常量引用可以使得变量拥有只读的属性。
(4) C++中的常引用可以用常量初始化而得到一个只读变量。
(5) C++中引用的本质是一个指针常量。
课后练习:
l 在C++中为什么不允许引用数组Type& a[N]?
l 如何定义一个数组的引用?如何定义一个函数的引用?
l 数组引用和数组指针有什么区别?函数引用和函数指针又有什么区别?