目录
6.1 函数基础
静态局部对象 static
类型
用static
声明后,变量的生命周期就是:从变量创建到程序结束。
注:static
类型变量默认初始化为0
编程习惯!
关于函数的声明和定义:
小结:函数应该在头文件中声明,在源文件中定义。
附:声明和定义的区别
6.2 参数传递
编程习惯!
关于引用传递的参数类型:const
型
const
实参和形参
函数不会改变的形参如果不是const
类型,则会有很多限制!
- 被误认为这个参数是可以修改的!
- 极大限制了函数所能接受的实参类型:
void test1(int& a) // 非const
{
cout << a << endl;
}
void test2(const int& a) // 非const
{
cout << a << endl;
}
void test()
{
test1(10); // 错误!因为不能用字面值和非const引用绑定!
test2(10); // 正确!因为test2的参数是const型
const int a = 10;
test1(a); // 错误!因为不能用const变量和非const引用绑定!
test2(a); // 正确!因为都是const
}
因此,可以发现,如果函数的形参只是int&
,那么只有int
型的实参才能与之绑定;如果是const int&
,那么字面值、int
型、const int
型都可以与之绑定。
main
函数的形参
数组作为参数
两个特性:
- 不允许拷贝数组 --> 无法值传递,只能引用传递
- 使用数组的时候,会转为指针 --> 传一个数组,实际上传的是首元素指针
标准库规范:传递指向首元素和尾元素的指针
例子:
int &test1(int *a, int index) // 返回引用, 第一个参数是数组首元素的地址
{
return a[index]; // 返回第index个元素的引用
}
void test()
{
int a [] = {1,2,3,4,5};
for (auto i : a)
{
cout << i << " ";
}
cout << endl;
test1(a, 1) = 9; // 传入数组a,但会自动转为a[0]的地址; 因为左边是一个引用,因此可作为左值,因此可以直接赋值
for (auto i : a)
{
cout << i << " ";
}
}
6.3 返回类型和return语句
错误的return
尾置返回类型
记号:->
string a = "wind";
auto test(int *b) -> decltype(a) // auto的类型,就是根据decltype(a)的返回类型得到
{
return a;
}
int main()
{
int b[] = { 1,2,3,4 };
auto c = test(b);
cout << c;
return 0;
}
下面三个函数等价:
// 结合decltype,依据模板参数推导返回类型
template<typename ArgType1, typename ArgType2>
auto Func1(ArgType1& a, ArgType2& b) -> decltype(a + b)
{
return (a + b);
}
// 在C++14中可省略箭头返回值部分,直接将函数返回类型设置为auto
template<typename ArgType1, typename ArgType2>
auto Func2(ArgType1& a, ArgType2& b)
{
return (a + b);
}
// 也可以直接将返回类型定义为 decltype(auto)
template<typename ArgType1, typename ArgType2>
decltype(auto) Func3(ArgType1& a, ArgType2& b)
{
return (a + b);
}
6.4 函数重载
重载和const形参
只要记住:只有引用和指针作为形参,才可以重载!否则,都不行!
并且,会根据传入的顶层const
类型去选择使用哪个重载函数:
// 正确
void test(int& i)
{
cout << " 1 --- " << i << endl;
}
void test(const int& i)
{
cout << " 2 --- " << i << endl;
}
// 错误
void test1(int i)
{
cout << " 1 --- " << i << endl;
}
void test1(const int i)
{
cout << " 2 --- " << i << endl;
}
int main()
{
const int a = 10;
test(a); // a是const,因此选择void test(const int& i), 输出 2 --- 10
return 0;
}
const_cast
与重载
const_cast
常用在函数重载中!主要作用是:const
型和非const
型的相互转换。
使用的初衷:通常不希望修改传入的string
,因此形参被返回类型都被定义为const
,然而有时候需要返回一个非const
的string
,因此就需要用const_cast
过渡一下。
string &test(const string &s1, const string &s2) // 错误,因为传入是const,返回必须是const
{
return s1.size() > s2.size() ? s1 : s2;
}
正确:
const string &test(const string &s1, const string &s2)
{...}
int main()
{
string s1 = "wind", s2 = "wind1";
string &s = test1(s1, s2); // 错误,因为test返回的是const,因此必须是const型接收
const string &s = test1(s1, s2); // 正确
cout << s;
return 0;
}
我们不希望修改s1
和s2
,因此test()
要以const
类型传入;然而我们希望第13
行的s
是可以修改的,但又必须用const string
去接收,此时就不好办了。因此,要用const_cast
过渡一下:
const string &test(const string &s1, const string &s2)
{
return s1.size() > s2.size() ? s1 : s2;
}
string& test1(string& s1, string& s2)
{
auto& r = test(const_cast<const string&>(s1), const_cast<const string&>(s2));
return const_cast<string&>(r);
}
int main()
{
string s1 = "wind", s2 = "wind1";
string &s = test1(s1, s2);
cout << s;
return 0;
}
第8
行,用先用const_cast<const string&>
把非const
的s1、s2
转为const
,调用test()
,用一个const string &r
去接收,然后再用const_cast<string&>
把const
的r
转为非const
的r
。
作用域对重载的影响
一句话总结:如果在函数内层作用域中对函数做了重载,那么就会忽略所有函数外的函数重载。
只有把重载函数放在一个作用域中(例如都在函数外或函数内),才不会被忽略:
6.5 特殊用途
内联函数inline
主要作用:很多条件判断语句可以通过写一个小的函数去等价表示,这样更加清晰明了,也便于后期修改;然而函数调用是需要占用一定资源的(运行更慢),因此内联函数就用来解决这个问题。
关键字:inline
定义内联函数:
调用:
以上表面,对于较大的函数(比如有一百行),即使声明为inline
,但编译器也可能不会当做内联函数使用,不然更费时间。
注:对于inline
函数和constexpr
函数,通常把声明和定义放在头文件中,因为这两个函数在程序中需要被多次定义,而这些定义必须一致!
6.7 函数指针
概念
顾名思义,就是指向函数的指针,这个指针可以代替函数来使用。
写法和用法
声明:
类型 (*指针名称)(形参);
例如:
const string& compare(const string& s1, const string& s2)
{
return s1.size() > s2.size() ? s1 : s2;
}
void test()
{
const string& (*p)(const string& s1, const string& s2);
p = compare;
cout << p << endl << &compare << endl; // 输出相同
string b1 = p("wind", "wind1");
cout << b1 << endl;
}
第1
行,是一个函数;
第8-9
行,就是定义一个函数指针p
,指向第1
行的函数;
注意:函数指针的返回类型、形参必须和函数定义保持一致
函数: const string& compare(const string& s1, const string& s2)
函数指针: const string& (*p)(const string& s1, const string& s2);
指针指向函数:p = compare;
第13
行,使用函数指针p
,代替直接调用函数compare()
注1:函数指针可以初始化为0
或nullptr
(因为本质上就是指针,遵循指针的初始化原则)
注2:重载函数指针时,必须清晰界定选用哪个函数,包括形参和返回类型:
函数自动转为函数指针
再看decltype
decltype
只会返回类型,不会做任何数据转换!!因此很多时候需要手动转换!例如:
函数指针那里:
const string& compare(const string& s1, const string& s2)
{
return s1.size() > s2.size() ? s1 : s2;
}
void test()
{
const string& (*p)(const string& s1, const string& s2);
p1 = compare;
string b1 = p1("wind", "wind1");
cout << b1 << endl;
decltype(compare) *p2 = p1; // 重点在这里
cout << &p2 << endl << &p1 << endl; // 地址不一样,不是同一个指针
string b2 = p2("wind", "wind1");
cout << b2 << endl;
}
第14
行,decltype(compare)
返回的是一个函数类型,因此需要加上*
才能得到指针
附:部分习题答案
练习6-10
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <string>
#include <vector>
void test1(int* a, int* b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void test()
{
int a = 10, b = 20;
test1(&a, &b);
cout << a << endl << b << endl;
}
int main()
{
test();
return 0;
}
练习6-12
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <string>
#include <vector>
void test1(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void test()
{
int a = 10, b = 20;
test1(a, b);
cout << a << endl << b << endl;
}
int main()
{
test();
return 0;
}
练习6-33
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <string>
#include <vector>
// 我的答案 -- 写复杂了
vector<int>::iterator test1(vector<int>v, vector<int>::iterator begin, vector<int>::iterator end)
{
if (begin + 1 == end)
{
cout << *begin << endl;
return begin;
}
cout << *begin << endl;
return test1(v, begin + 1, end);
}
// 参考答案
void print(vector<int>v, int index)
{
int size = v.size();
if (index != size)
{
cout << v[index++] << endl;
print(v, index);
}
}
void test()
{
vector<int>v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
/// 调用我的答案
//vector<int>::iterator begin = v.begin();
//vector<int>::iterator end = v.end();
//vector<int>::iterator iter = test1(v, begin, end);
/// 调用参考答案
int index = 0;
print(v, index);
}
int main()
{
test();
return 0;
}
练习6-36
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <string>
#include <vector>
// 6.36
string(&func())[10];
// 6.37
// 类型别名
typedef string arrT[10];
using arrT1 = string[10];
arrT& func();
arrT1& func();
// 尾置返回类型
auto func()->string(&)[10];
// decltype关键字
string str[10];
decltype(str)& func();
int main()
{
return 0;
}
练习6-54
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <string>
#include <vector>
void test()
{
// 函数的声明
int func(int, int);
// vector对象的声明
vector< decltype(func)* >v;
}
int main()
{
return 0;
}
练习6-55和6-56
#include <iostream>
#define _CRT_SECURE_NO_DEPRECATE
using namespace std;
#include <string>
#include <vector>
int func1(int a, int b)
{
return a + b;
}
int func2(int a, int b)
{
return a - b;
}
void test()
{
vector< decltype(func1)* >v;
decltype(func1)* p1 = func1;
decltype(func1)* p2 = func2;
v = { p1, p2 };
for (auto i : v)
{
cout << (*i)(1, 1) << endl;
}
}
int main()
{
test();
return 0;
}