前面我们已经提到过引用,这里我们再深入研究。
一 引用
引用就是某一(对象)的一个别名,对引用的操作与对变量直接操作完全一样,引用可以看成另一种指针。
int x=10;
int &rx=x;
将引用rx声明为int型变量x的别名,在声明引用时要注意:
(1)一经声明,不能修改,c++不允许在声明完毕后修改引用的值。
(2)声明引用时,必须同时对其初始化,因为 一经声明,不能修改,所以引用的初始化化是必须得。无初始化的引用是无效的。
(3)声明语句中的类型标识符必须与初始值的变量类型一致(特殊情况见后面)。
二 引用的使用限制:
1.不能建立数组的引用。数组是若干个元素所组成的集合,没法建立一个数组的别名。
2.不能声明一个引用的引用。
int & & refNum=num; //非法
3.声明指向引用的指针
int num=10;
int & *pNum=num; //非法
但是可以声明指针的引用。
#include <iostream>
using namespace std;
int main()
{
int num=10;
int *pNum=&num
int *&refNum=pNum;
cout<<*refNum<<endl; //10
return 0;
}
三 其它
C++程序可以对用new申请的动态无名实体建立一个引用。
#include <iostream>
using namespace std;
int main()
{
int &refNum=*new int(10);
cout<<refNum<<endl; //10
return 0;
}
在撤销动态内存时,可以用 delete &refNum
#include <iostream>
using namespace std;
int main()
{
int &refNum=*new int(10);
delete &refNum;
return 0;
}
对new申请的动态数组建立引用是非法的。
前面说过声明语句中的类型标识符必须与初始值的变量类型一致,当两者不一样时,有时候聪明的编译器会指出错误,除非引用的是用const修饰的,这种情况下c++将产生临时变量,下面用法是合法的。
#include <iostream>
using namespace std;
int main()
{
const int & refInt=9; //去掉const 非法 (常量是不能修改的)
cout<<refInt<<endl;//9
int x=10;
const double & refDouble=x; //去掉const 非法
cout<<refDouble<<endl; //10
return 0;
}
const 修饰的引用是只读的,没法改写。
四 指针与const
用const 修饰符声明的程序实体具有只读性,声明一个指针时,通过在声明语句的不同位置使用const效果是不一样的:
(1)禁止对指针赋值。
(2)禁止通过间接引用(*指针)对指针所指的变量赋值。
(3)即(1)又(2)。
1.禁止改写指针(常量指针或常指针)
在声明一个指针时,如果在*的右边加const修饰符,所声明的指针称为常量指针(常指针)编译器不允许改写指针的值,也就是说,改指针恒指向某个内存地址。
#include <iostream>
using namespace std;
int main()
{
int num=10,num2=20;
int * const pNum=&num
cout<<*pNum<<endl; //10
pNum=&num2; //非法
return 0;
}
在整个程序执行过程中,pNum的值无法改变,也就是没办法指向其它内存单元,但是可以间接修改变量的值。
#include <iostream>
using namespace std;
int main()
{
int num=10,num2=20;
int * const pNum=&num
cout<<*pNum<<endl; //10
// pNum=&num2; //非法
*pNum=0;
cout<<num<<endl;
return 0;
}
声明一个常指针时,必须对其进行初始化,因为常指针在声明完成后没有办法修改,未进行初始化的常指针式没有意义的,编译器会报错。
2.禁止改写间接引用
在指针声明时,将const修饰符放在指针类型之前,便无法通过间接引用改写指针所指变量(但是变量直接修改),如:
#include <iostream>
using namespace std;
int main()
{
int num=5;
const int *pNum=# //也可以这样写 int const *pNum=&num
*pNum=10; //非法
num=10;
return 0;
}
3,既禁止改写指针,有禁止改写间接引用
3是上面两种的 ”并集“,那么语法也该是那种的并集-----果然如此
#include <iostream>
using namespace std;
int main()
{
int num=5,num2=10;
const int * const pNum=&num
*pNum=10; //非法
pNum=&num2; //非法
num=10;
return 0;
}
五 使用const 提高函数的健壮性
1.用const修饰函数的参数
如果参数做输出用,无论它是什么数据类型,也不论采用指针传递,还是引用传递,都不能加const,否则该参数将失去输出功能啦。
const只能修饰输入参数
(1)例如 StringCopy函数:
void StringCopy(char *strDestination,const *strSource)
其中strSource是输入参数,strDestination是输出参数,给strSource加上const修饰后,如果函数体内的语句试图改动strSource的内容,编译器要报错的。
(2)如果输入参数采用“值传递”由于函数将自动产生临时变量用于复制该参数,所以无需保护。
(3)对于非内部数据类型的参数而言,像 void fun(A a),则应该写成void fun(const A &a),既提高了效率,又安全。但是对于内部数据void fun(int x)就没必要那样写了。
2.用const 修饰函数的返回值
(1)如果给以“指针传递”的方式的函数返回值加const修饰函数返回值(即指针的内容不能被修改,该返回值只能赋给加const 修饰的同类型指针
#include <iostream>
using namespace std;
const int *fun()
{
int num=3;
int *pNum=&num
return pNum;
}
int main()
{
const int *p=fun(); //去掉const 错误
cout<<*p<<endl; //3
return 0;
}
(2)如果函数返回值采用值传递的方式,加const 修饰没有任何价值。
例如:不要把函数int Fun()写成 const int Fun()。
不要把函数A Fun() 写成 const A Fun()。
3.const 成员函数
任何不会修改数据成员的函数都应该声明为const ,如果编写const函数时,不慎修改了数据成员,或调用了其他非const成员函数,编译器会指出的。
#include <iostream>
using namespace std;
class fun
{
private:
int num;
public:
fun(int Num=0):num(Num)
{}
void Add(int elem)
{
num+=elem;
}
int GetCount() const
{
num-=1; //编译错误
Add(10); //编译错误
return num;
}
};
int main()
{
fun f;
f.Add(10);
cout<<f.GetCount()<<endl;;
return 0;
}