我们从最常见的开始介绍起。
从常数到函数再到类
1. const修饰的常量
const int a = 10;//初始化后不能再修改
2.const修饰的函数参数
void Show(const int &i)//比如我们只是想输出i的值,不希望该函数修改i的值
{
cout << i << endl;
}
3.const修饰的函数返回值
const double& Show(double &b)//希望返回值以后的使用都不能被修改
{
return b;
}
4.const修饰成员函数
double MyClass::Show(const MyClass &ml) const;
//在成员函数后面加上const表示不希望通过改成员函数修改对象的值
double MyClass::Show(const MyClass &ml) const;
{
cout << ml.val << endl;//只是输出而不希望修改对象的值的时候可以这样用
}
5.指针常量和常量指针
int a = 10;
const int *p = &a;//常量指针
int* const p = &a;//指针常量
常量指针和指针常量在下面介绍。
const类型的使用可以归纳为这五种。
易混淆点:
const指针(常量指针)以及被const修饰的指针(指针常量)
什么是const指针?什么是被const修饰的指针?如何区分const指针以及被const修饰的指针?它们有什么区别?
const指针(也叫做指针常量,即指针是一个常量,起所存储的地址无法改变)
int a = 10;
int* const p = &a;//const指针,const作用于指针
const指针无法改变指针本身所存储的地址,但是可以改变其所指向的变量的值
如:
int a = 10;
int b = 20;
int* const p = &a;
// 下面这种改变的方式是错误的,因为p是const的,也就是说不能改变p所存储的地址。
p = &b; // 错误:无法修改常量 'p'
// 下面是正确的,可以通过指针p来修改它所指向的内容
*p = 30; // 正确:可以修改p指向位置的内容
被const修饰的指针(也叫常量指针,即指向常量的指针)
//最常见的const和指针的搭配
int a = 10;
const int *p = &a;
被const修饰的指针无法改变其所指向的变量的内容,但是可以改变指针所存储的地址。
int a = 10;
int b = 20;
const int* p = &a;
// 下面这种改变是正确的,因为p本身不是const的,所以可以改变p所存储的地址。
p = &b; // 正确:可以修改p的值
// 下面是错误的,无法通过指针p来修改它所指向的内容
*p = 30; // 错误:无法修改p指向位置的内容
区分两种成员函数:
//如果说该对象的成员函数是一个数组,我希望可以直接取出该对象中数组的某个项的值
class MyClass
{
private:
enum{SIZE = 30};//或static const int SIZE = 30;
double arr[SIZE];
public:
double operator[](int i)const {return arr[i];}//返回的是一个右值
double &operator[](int i ) {return arr[i];}//返回的是一个左值
};
出个题:
int const *const p,这个是个什么玩意?
是一个既不能修改所存储的地址,也不能修改所指向的内容的指针。
举个例子:
int a = 10;
int b = 20;
// p是一个指向const int的const指针
int const *const p = &a;
// 以下操作是非法的:试图通过常量指针改变所指向的值;既不能修改指向的值
//*p = 20;
// 以下操作也是非法的:试图修改指针常量p的指向;也不能修改所存储的地址
//p = &b;
易错点:
如果形参是被const修饰的指针,那么这个指针是不允许修改所指向变量的值还是不允许修改指针本身所存储的地址?
举个例子:
#include <iostream>
using namespace std;
//如果传入的形参是const指针,那么可以通过const指针来修该指向的值吗?
void test(const int *p)
{
*p = 20;//试图使用指针来修改指针所指向的变量的值
cout << "change" << endl;
}
int main(void)
{
int a = 10;
int *p = &a;
cout << "a = " << a << endl;
test(p);
cout << "a = " << a << endl;
return 0;
}
可见,被const修饰的形参指针是无法修改所指向的值的,那么可以修改指针所存储的变量地址吗?
答案是可以的,也就是说指针本身可以改,但是你不能拿这个指针去改所指变量的值,原因是什么呢?
由上面const指针和被const修饰的指针可以知道,被const修饰的指针是可以改变所存储地址,但是不能改变所指向的内容的。
而且我认为是形参的const指针和实参的const指针根本不是一个东西,可以看到输出的地址根本不一样,只是所指向的内存空间一样罢了,你在函数里面修改形参的指针与外面实参的指针无关。
关于const是否会影响特征标,即有关const和函数重载的问题
只有引用和指针加const才会影响特征标,从而影响函数重载,而传入常量的话,加上const也会认为是一个函数。
举例:
#include <iostream>
using namespace std;
void f1(const int *p)
{
cout << "常量指针" << endl;
}
void f1(int* p)
{
cout << "指针p" << endl;
}
int main(void)
{
return 0;
}
上述代码可以编译通过,可见实现了函数重载。
#include <iostream>
using namespace std;
void f1(const int p)
{
cout << "常量p" << endl;
}
void f1(int p)
{
cout << "变量p" << endl;
}
int main(void)
{
return 0;
}
编译不通过,说明不是指针或者引用加上const是不会影响其特征标的。
我觉得主要原因是,加const不就是为了保护值不被改变吗?指针和引用都有可能会改变所指向变量的值,而变量作为参数是按值传递,形参和实参都不是一个变量,因此不可能说传变量可以通过形参改变实参,所有加const和不加const没啥区别,没有什么意义,因此编译器认为const int p和int p是同一个东西。
顶层const和底层const
底层const,比如常量指针(即修饰底层的数值)
int a = 10;
const int *p = &a;
顶层const,比如指针常量(修饰的是指针)
int a = 10;
int* const p = &a;
编译器是认得出指针常量和常量指针的
#include <iostream>
using namespace std;
void f1(const int *p)
{
cout << "常量指针" << endl;
}
void f1(int* const p)
{
cout << "指针常量" << endl;
}
/*
void f2(int n)
{
cout << "f2" << endl;
}
void f2(const int n)
{
cout << "f2" << endl;
}
*/
int main(void)
{
int a = 10;
const int *p1 = &a;
int * const p2 = &a;
f1(p1);
f1(p2);
return 0;
}