const用途:
- const常量/指针/引用(顶层const/底层const)
- const变量在其它文件中使用(内部连接/外部连接)
- const修饰函数参数
- const修饰函数返回值
- const修饰成员变量
- const修饰成员函数
1. const常量/指针/引用(顶层const 与底层const):
此部分代码来自 《C++中文版》第五版
#include<iostream>
using namespace std;
int main()
{
// 顶层const表示指针本身是个常量
// 底层const表示指针指向的对象是常量
int i = 0;
int *const p1 = &i; // 不能改变 p1 的值,这是一个顶层const
const int ci = 42; // 不能改变 ci 的值,这是一个顶层const
const int *p2 = &ci; //允许改变p2的值,这是一个底层const
const int *const p3 = p2; // 靠右的是顶层const,靠左的是底层const
const int &r = ci; // 用于声明引用的const都是底层const
// 当执行对象的拷贝操作时,常量是顶层还是底层const区别明显。其中,顶层const不受什么影响
i = ci; // 正确:这是一个顶层const,对此操作无影响
p2 = p3; // 正确:p2和p3指向的对象类型相同,p3顶层const的部分不影响
// 当执行对象拷贝时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型能够转换。
// 一般来说,非常量可以转化为常量,反之不可以。
// int *p = p3; // 错误:p3包含底层const的定义,而p没有
p2 = p3; // 正确,p2和p3都是底层const
p2 = &i; // 正确:int * 能转化为 const int*
// int &r = ci; // 错误:普通int&不能绑定到int常量上
const int &r2 = i; // 正确:const int& 可以绑定到一个普通int上
return 0;
}
2. const变量在其它文件中使用(内部连接/外部连接)
在C中,const是外部连接,在C++中,const是内部连接。
内部连接:
只对正在编译的文件创建内存空间,别的文件可以使用相同的标识符和全局变量,编译器不会发现冲突。
外部连接:
所有被编译过的文件常见一片单独的存储空间,一般全局变量和函数名的外部连接通过 extern 声明。
- .cpp的const为内部连接,可以在不同的.cpp中定义相同名称的const变量
// 1.cpp
const int a = 1; // ok
// 2.cpp
const int a = 2; // ok
- .c中const为外部连接,不可以在不同的.c中定义相同名称的const变量
// 报错:对象被多次指定
// 1.c
const int A = 0;
// 2.c
const int A = 0; // err
- .c/.cpp其它文件使用本文件的变量
// 1.c/1.cpp 定义
(extern) const int a = 0;
// 2.c/2.cpp 声明
extern const int a;
3. const修饰函数参数
如果参数作为输出用,无论它是什么数据类型,都不能加const修饰
参数为输入时:
1.指针传递:
- 加const可以防止意外的改动指针,起保护作用
// 程序参考网址:https ://blog.csdn.net/qq_25361489/article/details/79934939
#include<stdio.h>
// strIn 输入参数,加const防止改动指针
// strOut 输出参数,不加 const 修饰
void StringCopy(char *strOut, const char *strIn)
{
while ((*strOut++ = *strIn++) != '\0');
}
int main()
{
const char *a = "12345";
char b[25];
StringCopy(b, a);
for (int i = 0; *(b + i) != '\0'; i++)
{
printf("%c", *(b+i));
}
return 0;
}
2.值传递
- 如果采用值传递,不用加const修饰
- 因为函数会自动产生临时变量,用于复制该参数
void Func(int a)
{
//。。。
}
3. 引用传递
- 对于非内部数据类型的参数而言,值传递效率比较低,如Void Func(A a),采用引用传递
- 因为函数体内将产生 A 类型的临时对象用于复制参数 a,而临时对象的构造,复制,析构过程都将消耗时间。
- 采用引用传递则不需要产生临时对象
- 内部数据类型无限采用引用传递,因为内部数据类型的参数不存在构造、析构的过程,而复制也特别快。
void Func(A a)
{
//。。。
}
4. const修饰函数返回值
1.指针传递
如果以指针传递方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不可以被修改,该返回值只能被附加给const修饰的同类型指针。
const char* GetString()
{
return "123";
}
int main()
{
const char *a = GetString();
return 0;
}
2.值传递
不加const,因为函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值
3.引用传递
- 如果返回值不是内部数据参数,如A GetA(),写成 const A GetA(),会提高效率
但此时千万要小心,一定要搞清楚函数究竟是想返回一个对象的拷贝还是仅仅返回别名即可
#include<iostream>
using namespace std;
// const A &operator=(const A &other)
// a = b = c; //正常链式关系,正确
// (a = b) = c; // 函数返回值不可修改,非法,错误
class A
{
public:
A &operator=(const A &other)
{
if (this != &other)
{
this->num = other.num;
}
return *this;
}
int num = 1;
};
int main()
{
A a, b, c;
c.num = 5;
a = b = c; //正常链式关系
(a = b) = c; // 非正常链式关系,但合法
cout << a.num << b.num << c.num << endl;
return 0;
}
5. const修饰成员变量:
#include<iostream>
using namespace std;
// const 修饰成员变量
class A
{
public:
A(int size) :i(size) {};
const int iiii = 8;
const int i; // const 修饰非静态成员变量,在构造函数中对该变量进行初始化
static const int ii = 5;
static const int iii; // const 修饰静态成员变量,在类外对该变量进行初始化
};
const int A::iii = 10;
int main()
{
A a(100);
cout << a.i <<endl; // 100
cout << a.ii << endl; // 5
cout << a.iii << endl; // 10
cout << a.iiii << endl; // 8
}
6. const修饰成员函数
-
任何不会修改成员变量的函数都应该声明为const类型
-
如果在编写const成员函数时,不慎修改了成员变量,或调用非const成员函数,编译器会报错,提高了程序的健壮性。
-
mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中
// 程序参考网址:https ://blog.csdn.net/qq_25361489/article/details/79934939
#include<iostream>
using namespace std;
class Stack
{
public:
int GetCount() const;
private:
int num = 3;
mutable int m_num = 5;
};
int Stack::GetCount() const
{
// mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
m_num = 1;
return num + m_num;
}
int main()
{
Stack stack;
int a = stack.GetCount();
cout << a;
return 0;
}
参考网址:
https://wenku.baidu.com/view/186fb4ef5ef7ba0d4a733bf9.html