6. 函数重载·内联函数·const
本文最后更新于 2024年1月28日 下午
函数重载·内联函数·const
这是浙江大学翁恺老师的公开课,《面向对象设计C++》
视频地址:
https://www.bilibili.com/video/BV1yQ4y1A7ts/?spm_id_from=333.337.search-card.all.click&vd_source=3074f6f6ab43a114c5af8727fa4f7255
本节对应视频17-20部分。
函数重载(function
overloading)指的是一些函数可以有相同的函数名,但是有不同的参数表,这些参数表中的参数的个数和类型可能都不一样,构成重载关系。在调用函数的时候,给出的不同的参数决定了编译器会调用哪一个函数。
1
2
3
4
5
6//如下的print函数都是重载的关系
void print(char *str, int width);
void print(double d, int width);
void print(long l, int width);
void print(int i, int width);
void print(char *str);
需要注意的是,如果两个函数的函数名相同,但是返回类型不同,这两个函数并不能构成重载的关系。
如果存在重载的函数,如果变量需要进行强制转换,那么编译器会试图找到完全匹配的类型。
缺省值
缺省值(default
arguments)是C++中的一个功能,在定义函数时可以在函数的参数表中预先给一个值,表示这个参数的默认值。
1
Stash(int size, int initQuantity = 0); //如果调用Stash函数时没有指定initQuantity,那么它自动为0
1
2int harpo(int n, int m =4, int j=5);
int chico(int n, int m=6, int j); // 这种写法是不合法的。.h
文件中才能设置函数的缺省值,在函数具体的.cpp
文件中不能存在缺省值。
f
的函数原型在a.h
中:
1
void f(int i, int j=0);
a.cpp
: 1
2
3
4
5
6
7
8
9# include "a.h"
# include <iostream>
using namespace std;
void f(int i, int j)
{
cout << i << " "<< j << endl;
}main.cpp
: 1
2
3
4
5# include "a.h"
int main() {
f(10);
return 0;
}
1
2
3$ g++ main.cpp a.cpp
$ ./a.out
>> 10 0
1
2
3
4
5
6
7
8# include "a.h"
# include <iostream>
using namespace std;
void f(int i, int j = 0) // 在a.cpp中设置函数的缺省值
{
cout << i << " "<< j << endl;
}
1
2
3
4
5$ g++ main.cpp a.cpp
$ ./a.out
>> a.cpp: In function 'void f(int, int)':
>> a.cpp:6: error: default argument given for parameter 2 of 'void f(int, int)'
>> a.h:1: error: after previous specification in 'void f(int, int)'a.h
中不设置缺省值,编译也无法通过。
1
2
3
4
5$ g++ main.cpp a.cpp
$ ./a.out
>> a.cpp: In function 'void f(int, int)':
>> a.h:1: error: too few arguments to function 'void f(int, int)'
>> main.cpp:5: error: at this point in file
现在思考这样的尝试,如果在main.cpp
中不使用a.h
而直接定义f
:
1
2
3
4
5
6// # include "a.h"
void f(int i, int j = 10)
int main() {
f(10);
return 0;
}
1
2
3$ g++ main.cpp a.cpp
$ ./a.out
>> 10 10
缺省值会造成程序的阅读困难而且不安全,因此不要使用缺省值。
内联函数
在函数调用的过程中会出现额外的开销(overhead)。这些开销与堆栈操作有关:每个程序都有自己独立的堆栈,用于存放本地变量和返回地址。在函数调用的过程中,堆栈做的操作有:
- 函数的参数入栈 - 返回地址入栈 - 准备返回值 - 出栈所有
这些堆栈操作额外的开销可以通过C++中的特性内联函数(inline function)解决:如果一个函数是内联函数,其在被调用的时候会将函数的代码嵌入到调用的地方,保持函数的独立性。
比如:
1
2
3
4
5
6
7
8
9// f 是一个内联函数
inline int f(int i){
return i*2;
}
main(){
int a=4;
int b=f(a);
}
1
2
3
4main(){
int a=4;
int b=a+a; //将f的实际指令嵌入其中
}
用法是在需要设置为内联的函数前加入关键字inline
,且必须在函数的声明*.h
和定义*.cpp
中都设置为inline
。
在函数声明中:
1
inline int plusOne(int x);
1
inline int plusOne(int x){return ++x;};
*.cpp
文件可以不被需要,否则会出现重复定义。
内联函数牺牲代码空间,但是会降低调用函数时候额外的开销,从而降低运行时间。但是在大多数时候,这是值得的。
如果成员函数在class
声明时就给出函数体,那么这些函数都是内联函数。
1
2
3
4
5
6
7
8
9
10
11class Point {
int i,j,k
public:
Point() {i=j=k=0};
Point(int ii, int jj, int kk){i=ii,j=jj,k=kk;}//内联函数
void print(string& msg = ''){ //内联函数
cout<<"i= "<<i<<","
<<"j="<<j<<","
<<"k="<<k<< endl;
}
};
被频繁调用或者非常小的函数(2-3行)值得做成内联函数。非常大的函数(20行)以及递归的函数不要做内联。
const
const
用于变量之前,表示这个变量被赋值之后其值不可以做任何修改:
1
2
3const int x=1;
x =2; //非法
x++; //非法const
变量的值需要在编译时让编译器知道(才能提前为本地变量分配内存大小)才能够通过编译:
1
2
3
4
5
6
7
8const int class_size = 12
int finalGrade[class_size]; // OK
```
```cpp
int x;
cin >> x; // 编译时编译器并不知道x的值
const int size = x;
double classAverage[size]; //error
const
指针
当一个指针变量是const
时,有两种写法:
1
2
3* const q = 'abc'; // q is const
*q = 'c'; // ok
q++; // errorq
本身是const
,*q
指向的内存的内容并不是const
,因此对应内存的内容可以改变,这种写法称为常量指针。
1
2
3const char *p = 'abc' // *p is a const char
*p='b'; // error
p++; // ok*p
指向的内存地址中的内容是const
的,不能通过p
去修改指向的内存单元,这种写法是指针常量。
const
的对象和成员函数
如果一个对象是cosnt
,表明这个对象内的值是不能被修改的。
1
const Currency the_raise(42,38);
const
表示这个函数不会修改成员变量的值。
1
int get_day() const;
1
2
3int get_day() const {
return day;
};const
其实加在了this
前,表示的是this
是const
:
1
2
3
4
5
6
7
8
9
10
11
12
13class A {
int i;
public:
A() : i(0) {} // 初始化i
void f() { cout << 'f()'<< endl;} //相当于f(A* this)
void f() const {cout << 'f() const'<< endl;} //相当于 f(const A* this)
// 因此两者参数表不同,构成重载关系
};
int main(){
const A a; // a是const,因此选择调用const的f()
a.f();
return 0;
}1
>> f() const
const
,其一定要被初始化(因为其他没有再修改它的值的机会了)。