const整理_与_函数重载
-
const对象一旦创建其值不可改变,所以const对象必须初始化。
-
默认情况,const对象仅在本文件内有效。不同文件中出现同名const变量,等同于不同文件分别定义的变量。使用extern关键字可以在不同文件共享const变量。
1
2
3
4
5// file_1.cc定义并初始化一个常量,该常量能被其他文件访问
extern const int bufSize = fcn();
// file_1.h头文件
extern const int bufSize; // 与file_1.cc中定义的bufSize是同一个
const的引用(常量引用、指向常量的引用)
1 | const int ci = 1024; |
指向常量的指针
1 | const double pi = 3.14; |
- 可以这样理解,“指向常量的指针”与“const的引用”,不过是指针和引用“自以为是”罢了,它们觉得自己指向了常量(实际未必)。不能通过它们两个去改变所指向对象的值,但可以通过改变所指对象的值进而改变它们两个。
常量指针(const指针)
- *后加const表常量指针
- 常量指针必须初始化
- 常量指针指向的地址不可变,但指向的内容随意改变
1 | int test = 233; |
- 顶层const表示指针本身是一个常量;底层const表示指针所指向的对象是一个常量。
constexpr变量
请拜读C++ Primer原著…
const形参和实参
1 | // 调用fcn函数时,实参可以传const int,也可传int |
-
调用函数时,形参的初始化规则与变量的初始化规则是一样的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 变量的初始化规则
int i = 42;
const int *cp = &i; // 正确,但是不能通过cp去改变i
const int &r = i; // 正确,但是不能通过r去改变i
const int &r2 = 42; // 正确
int *p = cp; // 错误,可通过改变 *p 进而导致改变 *cp,最终改变 i,这是不允许的
int &r3 = r; // 错误,同上
int &r4 = 42; // 错误,不能用字面值初始化一个非常量引用
// 上述变量的初始化规则同样适用于形参的初始化
void reset(int *ip) { *ip = 0 }
void reset(int &i) { i = 0 };
int i = 0;
const int ci = i;
reset(&i); // 正确,调用了reset(int *ip)
reset(&ci); // 错误,reset(int *ip)可改变指针所指向变量的值,这里ci不允许
reset(i); // 正确,调用了reset(int &i)
reset(ci); // 错误,reset(int &i)可改变引用变量的值,这里ci不允许
reset(42); // 错误,reset(int &i)不能把字面值初始化一个非常量引用 -
把函数不会改变的形参定义成普通引用是一种比较常见的错误,这么做会给函数调用者一种误导,使其认为函数可以修改它的实参值。
函数重载
-
如果同一作用域内,几个函数名字相同但形参列表不同,称之为重载函数。
- 形参列表不同指形参数量不同或形参类型有所不同。
- 不允许两个函数除了返回类型外其他所有的要素都相同,第二个函数的声明是错误的。
-
main函数不能重载。
-
有时候两个形参列表看起来不一样,但实际是相同的东西:
1
2
3
4
5
6
7// 以下两组内容不构成重载
void func(const Account &acct);
void func(const Account&); // 与上形参列表相同,只是省略了形参的名字
typedef Phone Telno;
void func(const Phone&);
void func(const Telno&); // 与上形参列表相同,Telno与Phone类型相同 -
重载与const形参
-
一方面,顶层const不影响传入函数的对象,拥有顶层const的形参无法和另一个没有顶层const的形参区分开来:
1
2
3
4
5void func(Phone);
void func(const Phone); // 重复声明
void func(Phone*);
void func(Phone* const); // 重复声明 -
另一方面,如果形参是指针或引用,可通过区分其指向的是常量对象还是非常量对象来实现函数重载。此时const是底层的。(这一概念与**“基于const的重载”**非常类似,详见博客“类与结构体”一篇的总结。)
1
2
3
4
5
6
7
8
9
10/*
编译器可以通过实参是否是常量来推断应该调用哪个重载版本。
因const不能转换成其他类型,我们只能把const对象(或指向const的指针)传递给const形参。
相反,因非常量可以转换成const,所以下面的4个函数都能作用于非常量对象或非常量对象的指针,但编译器会优先选用非常量的重载版本。
*/
void func(Account&);
void func(const Account&);
void func(Account*);
void func(const ACCOUNT*);
-
-
重载与作用域
-
重载对作用域的一般性质并无改变:如果我们在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。在不同作用域中无法重载函数名。
1
2
3
4
5
6
7
8
9
10
11
12
13string read();
void print(const string &);
void print(double); // 外层作用域重载print函数
void func() {
bool read = false; // 内层作用域,隐藏了外层的read函数
string s = read(); // 错误,read此时只是一个布尔值,而非函数
void print(int); // 内部作用域,隐藏了外层的print函数(因在不同作用域无法重载同一函数名,不涉及形参列表的比较问题,因此只要名字相同就会进行覆盖)
print("tempstr"); // 错误,外层的print(const string &)函数被隐藏掉了
print(233); // 正确,调用内部作用域的print(int)
print(3.14); // 正确,注意,double类型的实参会转换成int类型,调用内部作用域的print(int)。外层的print(double)被隐藏掉了。
} 当我们调用print函数时,编译器首先寻找对该函数名的声明,找到的是接收int值的那个内部作用域声明。一旦在当前作用域中找到了所需名字(名字相同即可),编译器就会忽略掉外层作用域中的同名实体(被隐藏)。剩下的工作就是检查函数调用是否有效了(即形参实参是否匹配,函数能否调用成功)。
-
评论