引用
引用的定义
引用给对象起了另外一个名字,即所谓的别名。引用并非对象,它只是为一个已经存在的对象所起色另一个名字。
定义引用时,程序把引用和 它的初始值绑定在一起,而不是将初始值拷贝给引用(关于这点尤其是在自定义的类时需要注意,构造函数与拷贝构造函数)。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。
引用的类型必须和与之绑定的对象严格匹配(除了父类指向子类的引用的情况)。而且,引用只能绑定在对象上,不能与字面值和某个表达式的计算结果绑定在一起。
引用在程序中的几种定义
int iVal = 1024;
int &refVal = ival; //正确,refVal是引用,指向iVal,是IVal的别名
int &refVal2; //报错,引用必须初始化
refVal = 2; //正确,把2赋值给refVal指向的对象
int ii = refVal; //正确,与 ii=iVal 执行结果一样
int &refVal3 = refVal; //正确,把refVal3绑定到refVal指向的对象
int i = refVal; //正确,i初始化为IVal的值
int refVal4 = 10; //报错,引用类型的初始值必须是一个对象
double dVal = 3.14;
int &refVal5 = dVal; //报错,此处引用类型的初始值必须是一个int型对象
const 修饰指针
一般分为如下四中情况:
int b = 500;
const int * a = &b; //情况(1)
int const * a = &b; //情况(2)
int * const a = &b; //情况(3)
const int * const a = &b; //情况(4)
- 对于情况(1)和情况(2):
如果const 位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
因此情况(1)和情况(2)相同,都是指针所指向的内容为常量,这种情况不允许对内容进行更改操作。
int b = 500;
const int * a = &b;
*a = 600; //错误
虽然不能修改指针a所指向的内容,但是可以通过将指针a指向别处来改变*a的值
int b = 500, c = 600;
const int * a = &b;
a = &c;
- 对于情况(3):
只恨本身是常量,这种情况下不能指针本身进行更改操作,而指针所指向的内容不是常量。
对于情况(3),定义时必须同时初始化。
int b = 500, c = 600;
int * const a; //错误,没有初始化
int * const a = &b; //正确,必须初始化
*a = 600; //正确,允许更改*a的值
a = &c; //错误,不能更改指针a本身,
- 对于情况(4),指针本身和指针所指向的内容均为常量。
const 修饰函数参数
const 修饰函数参数,表示该参数不能修改
int foo(const int x, int y)
{
x = 2; //错误,x使用const修饰
y = 3; //正确
}
const成员函数
- const 放在函数声明后面
成员函数变为“只读”函数,不能修改数据成员的值,一旦试图修改数据成员的值,则编译器按错误处理。即常成员函数既不能修改数据成员的值,也不能调用那些能引起数据成员变化的成员函数,只能调用const成员函数。
class Point
{
int xVal, yVal;
public:
int GetY() const;
};
int Point::GetY() const
{
return yVal;
}
如果GetY() 试图用任何方式修改yVal的值或调用另一个非const成员函数,编译器会报错。
const和#define的区别:
C++可以用const也可以用#define来定义常量。但是前者有更多优点:
- const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意料不到的错误(边际效应)
- 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++程序中只使用const常量而不是用宏常量。