第二章 基本语言
第六节 函数新特性、内联函数、const详解
1. 函数回顾与后置返回类型
-
函数定义中,形参如果在函数体内用不到的话,则可以不给形参变量的名字,只给其变量
-
函数声明又叫函数原型
-
把函数返回类型放到函数名字之前,这种写法就是前置返回类型
-
c++11中引入了一种新的写法,后置返回类型:就是在函数声明和定义中,把返回类型写在参数列表之后,
前面放auto,表示函数返回类型放到参数类表之后
#include <iostream>
#include <cstring>
using namespace std;
//auto :变量自动类型推断
//函数声明
auto func123(int a, int b) -> void;
auto func123(int a, int b) -> void {
cout << "后置函数类型" << endl;
return;
}
int main() {
func123(1, 2);
return 0;
}
2. 内联函数
-
函数定义前添加关键字inline,导致该函数变成了内联函数
-
函数体很小,调用有很频繁,就需要引入inline(内联)函数
-
inline影响编译器,在编译阶段对inline这种函数进行处理,系统尝试将调用该函数的动作替换为函数本体,通过这种方式来提升性能
-
inline只是开发者对编译器的一个建议,编译器可以尝试去做,也可以不去做,取决与编译器的诊断功能,决定权在编译器
-
内联函数定义要放在头文件中,这样需要源代码用到内联函数的.cpp文件都能够通过include把内联函数的包含进来,
以便找到这个函数的本体源代码并尝试将该函数的调用替换为函数体内的语句
优缺点
-
提升效率
-
代码膨胀,所以内联函数函数体尽量要小
-
注意: 不同编译器对inline函数处理不同,inline函数尽量简单,代码尽可能少,减少循环,分支递归调用尽量不要出现在inline函数中,编译器有可能拒绝这个函数成为inline函数
-
constexpr函数: constexpr int mf(),需要写的非常简单,可以看成是更严格的一种内联函数
-
#define宏展开也类似内联函数,类型检查?
3. 函数杂合用法总结
- 函数返回类型为void,表示函数不返回任何类型,但是我们可以调用一个返回类型是void函数让他作为另外一个返回类型是void的函数返回值
#include <iostream>
#include <cstring>
using namespace std;
void funca() {
}
void funcb() {
// 可行
// return;
return funca();
// 不可行
// return void;
}
int main() {
return 0;
}
- 函数返回指针和返回引用的情况
#include <iostream>
#include <cstring>
using namespace std;
int *myfunc() {
int tempvalue = 9;
return &tempvalue;
}
int main() {
int *p = myfunc();
// 函数调用完成后,内存已经被释放了, 所以这个地址是不可空的,程序会崩溃
// 解决方案是把 int tempvalue = 9 写成全局变量
*p = 6;
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int &myfunc() {
int tempvalue = 9;
cout << &tempvalue << endl;
return tempvalue;
}
int main() {
int &k = myfunc();
//还是不属于可控的地址,不能这么使用
cout << k << endl;
return 0;
}
- 视频中是windows系统,下面代码可行,可是LZ是lunux系统,下段代码还是会崩溃,所以这种综合考虑来看,慎用!
#include <iostream>
#include <cstring>
using namespace std;
int &myfunc() {
int tempvalue = 9;
cout << &tempvalue << endl;
return tempvalue;
}
int main() {
int k = myfunc();
cout << k << endl;
return 0;
}
- 不带形参的函数定义:没有形参可以表示形参列表为空,或者(void)
#include <iostream>
#include <cstring>
using namespace std;
int myfunc(void) {
return 1;
}
int main() {
int k = myfunc();
cout << k << endl;
return 0;
}
-
如果一个函数不调用的话,则该函数可以只有声明部分,没有定义部分
-
普通函数(非内联函数)定义只能定义一次,声明可以声明多次,一般函数定义.cpp文件会include自己的函数声明.h文件
-
引用做函数形参 void func(int &ta, int &tb),使用引用可以提升效率,函数可以返回多个返回值,在c++中更习惯用引用类型的形参,来取代指针类型的形参,提倡在c++中多使用引用类型的形参
#include <iostream>
#include <cstring>
using namespace std;
struct student {
int num;
// ...
};
void func(struct student &stu) {
stu.num = 100;
}
int main() {
struct student stu0;
func(stu0);
cout << stu0.num << endl;
return 0;
}
- c++中,函数允许同名,但是形参列表的参数类型或者数量应该不同
#include <iostream>
#include <cstring>
using namespace std;
void func0(int i) {}
//void func(const int i){} 这种重名,不可行
void func0(float i) {}
void func0(int i, int j) {}
int main() {
return 0;
}
4. const char*, char const*, char* const三者的区别
- 原始代码
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char str[] = "I am Felaim!";
char *p;
p = str;
*p = 'U';
p++;
cout << str << endl;
return 0;
}
- const char *p, p指向的目标不能通过p来改变
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char str[] = "I am Felaim!";
const char *p;
p = str;
// 这段相当于用p来修改p指向的内存
// *p = 'U';
// 但是p指向的地址可以修改
p++;
str[0] = 'U';
cout << str << endl;
return 0;
}
-
char const * 等价于 const char *
-
char * const p, p指向的内存是固定的,不能改变,但是指向内存的内容可以修改
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char str[] = "I am Felaim!";
// 定义的时候必须初始化, p一旦指向一个内存之后,就不可以指向其他内存
char *const p = str;
// p可以修改对应指向内存的内容
*p = 'U';
// p指向的地址不可以修改
// p++;
cout << str << endl;
return 0;
}
- const char* const p:p指向的地址和指向地址的内存均不可以改变
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char str[] = "I am Felaim!";
// 定义的时候必须初始化, p一旦指向一个内存之后,就不可以指向其他内存
const char *const p = str;
// p不可以修改对应指向内存的内容
// *p = 'U';
// p指向的地址也不可以修改
// p++;
cout << str << endl;
return 0;
}
- 其他引用与const的用法范例
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int i = 100;
// 代表a的内容不能修改
const int &a = i;
// 不可行
// a = 400;
//不可行
// int &b = 31;
//可行方案,给b分配了内存,所以可行
const int &b = 31;
cout << &b << endl;
//可行
int &c = i;
return 0;
}
5. 函数形参中带const
- 优点:
-
防止无意中修改形参值,导致实参值被无意修改
-
实参类型可以更加灵活(既可以使用普通类型,也可以使用常量类型即加const的变量)
- 范例一
#include <iostream>
#include <cstring>
using namespace std;
typedef struct student {
int num;
// ...
} student;
// 学习在形参中增加const的写法的习惯
//void func(const student &stu) {
// // 不会无意的修改其形参值
// // stu.num = 10086;
//}
void func(student &stu) {
stu.num = 10086;
}
int main() {
student stu1;
stu1.num = 1000;
func(stu1);
cout << stu1.num << endl;
const student &stu2 = stu1;
// 如果使用func(student &stu)则会报错
// func(stu2);
return 0;
}
- 范例二
#include <iostream>
#include <cstring>
using namespace std;
void func2(int &i) {
}
void func3(const int &i) {
}
int main() {
int aaa = 10;
// 可行
func2(aaa);
// 不可行
// func2(2);
// 可行, 进一步验证实参类型可以更加灵活
func3(2);
return 0;
}