函数声明
如果一个函数永远不会被我们用到,那么他可以只有声明没有定义.
在头文件中进行函数声明
1.函数应该在头文件中声明而在源文件中定义.
2.如果吧函数声明放在头文件中,就能确保同一函数的所有声明保持一致,而且一旦我们想
改变函数的接口,之需要改变一条声明即可.
3.定义函数的源文件应该把函数声明的头文件包含进来,编译器负责检验函数的定义和声明是否匹配.
引用传递和值传递
引用传递
当形参是引用类型的时候,我们就说他对应的实参是引用传递或者函数被引用调用.
值传递
当实参的值被拷贝给形参时,我们就说实参被值传递
使用引用避免拷贝
使用大的类类型或者容器对象比较低效,有的类类型根本不支持拷贝,当某种类型不支持拷贝操作的时候,函数只能通过引用形参访问该
类型的对象.当函数无需修改引用形参的值时,最好将其声明为引用常量
const 形参和实参
和其他初始化过程一样,当用实参初始化形参时会忽略掉顶层const,换句话说,形参的顶层const被忽略掉了,当形参有顶层const,传给他常量对象或者非常量
对象都是可以的.
其实也就是跟初始化一个变量和指针差不多
例子
void fcn(const int i){
};
void fcn(int i){
}; //这里直接报错重复定义
允许我们定义若干具有相同名字的函数,不过前提是不同函数的新参列表应该有明显的区别
初始化和对常量引用
一般的引用
引用的类型必须要与其引用的对象的类型一致
常量引用
1.在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可
2.允许为一个常量引用绑定非常量的对象,字面值,甚至是一个表达式.
const int &i = 520; //这个是ok的
标题##数组形参
void print(const int*);
void print(const int[]);
void print(const int[10]);
实际上上面的三个函数声明是等价的,每个函数的唯一参数都是const int*类型的.
数组引用实参数
void print(int (&arr)[10])
{
for(auto elem : arr)
cout<< elem << endl;
}
int k[10] = {
1,2,3,4,5,6,7,8,9,10};
print(k);
返回类类型的函数和调用运算符
调用运算符的优先级与点运算符和箭头运算符相同,并且也符合左结合律.
列表初始化返回值
c++11新标准,函数可以返回花括号包围的值的列表.
cstdlib中两个预处理变量
int main()
{
if(some_failure)
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}
因为他们是预处理变量,所以既不能在前面加上std::,也不能在using声明中出现.
返回数组指针
函数可以返回数组的指针或引用
使用类型别名简化处理
typedef int arrT[10]; // arrT是一个类型别名,他表示的类型是含有10个整数的数组
using arrT = int[10]; // arrT的等价声明
arrT* func(int i); // func返回一个指向含有10个整数的数组的指针
声明一个返回数组指针的函数
方法1
返回数组指针的函数形式如下
Type (*function (parameter_list)) [dimension];
int (*func(int i)) [10];
方法2
使用尾置返回类型
任何函数的定义都可以使用尾置返回,这种形式对于返回类型比较伏在的函数最有效.尾置返回类型跟在形参列表后面并以一个->符号
开头,我们在本应该出现返回类型的地方防止一个auto.
auto func(int i) -> int(*)[10];
方法3
使用decltype
int odd[] = {
1,3,5,7,9};
int even[] = {
0,2,,4,6,8};
decltype(odd) *arrPtr(int i)
{
return (i%2) ? &odd : &even;
}
decltype 并不负责吧数组类型转换成指针,所以decltype的结果是个数组.
函数重载
编译器根据传递的实参类型推断想要的是哪个函数,对于重载函数来说,他们应该在形参数量和形参类型上有所不同.
不允许两个函数除了返回类型外其他的所有的要素都相同
Record lookup(const Account &);
bool lookup(const Account &); //错误的
重载与作用域
string read();
void print(const string &);
void print(double );
void foobar(int ival);
{
bool read = false;
string s = read(); //错误,read是一个布尔值
void print(int); //新的作用域,隐藏掉之前的print函数
print("Value: "); //void print(const string &) 被隐藏起来,错误
print(ival); //调用的是void print(int);
print(3.14); // 调用的是void print(int);
}
在外层作用域中的print(const string &) 函数虽然于本次调用匹配,但是他已经被隐藏掉了,
根本不用考虑.
默认实参
调用含有默认实参的函数的时候,可以包含该实参,也可以省略该实参
注意
1:一旦某个形参被赋予了默认值,他后面的所有的形参都必须有默认值.
2.函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参.也就是说函数调用不能
填前面我们没有写的参数.
3.尽量让不怎么使用默认值的形参出现在前面,让经常使用的默认实参出现在后面
默认实参声明
string screen(sz,sz,char = ' ');
string screen(sz,sz,char = '*'); //这个是错误的
string screen(sz = 24,sz = 80,char ); //这个是正确的
对于函数的声明来说,多次声明同一个函数是合法的,但是在给定的作用域中一个形参
只能被赋予一次默认实参,后续声明只能为之前的没有默认值的形参添加默认
实参.而且右侧所有的形参都必须有默认值
内敛函数
内敛函数可以避免函数调用的开销,通常就是将他在调用点上"内敛的"展开.
cout << shorterstring(s1, s2) << endl;
coun << (s1.size() < s2.size() ? s1 : s2) << endl;
在函数的开头加上inline就变成了内敛函数
下面是书写的格式
inline const string &
shorterstring(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
实参类型匹配
需要类型提升和 算数类型转换的匹配
void ff(int);
void ff(short);
ff('a'); // char 提升成int,调用f(int);
假设有两个函数,一个接受int,另外一个接受short,当只有调用提供的是short类型的
值的时候才会选择short版本的函数即使实参是一个很小的整数值,也会直接提升为int
类型.所有算数类型的转换的级别都是一样的
函数匹配和const实参
Record lookup(Account&);
Record lookup(const Account&);
const Account a;
Account b;
lookup(a); //调用const版本
lookup(b); //非const,因为如果调用const版本需要类型转换
对于指针也是一样的.
函数指针
要想声明一个函数指针,只需要用指针替换函数名即可
重载函数的的指针
void ff(int *);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff;
void (*pf2)(int) = ff; // 错误,没有任何一个ff与该形参列表匹配
double (*p3)(int*) = ff; //错误
指针类型必须与重载函数中的某一个精确匹配
函数指针形参
void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &));
void useBigger(const string &s1, const string &s2, bool (*pf(const string &, const tring &)));
//上面两个声明是等价的
返回指向函数的指针
方法1
using F = int (int*, int); //F是类型
using PF = int(*) (int*, int); //PF是函数指针
PF f1(int);
F *f1(int);
方法2
int (*f1(int))(int*, int);
auto f1(int) ->int (*)(int*, int);
方法3:将auto 和 decltype 用于函数指针类型
string::size_type sumLgength(const string&, const string&);
string::size_type largerLength(const string&, const string&);
decltype(sumLength) *getFch(const string&);
注意:当我们将decltype作用与函数的时候,他返回函数的类型而非指针类型,因此我们要加上*.