函数
函数的参数
可变形参的函数,如果函数的参数类型相同,可以使用initialize_list
的标准库类型,头文件与之同名。
一般操作:
initializer_list<T> lst
初始化initializer_list<T> lst{a,b,c}
初始值,传递列表元素的副本,列表的元素是const
lst2(lst)
拷贝或赋值一个initializer_list
对象,不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素。lst.size()
列表元素的数量lst.begin()
首元素的指针lst.end()
尾指针
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void error_msg(initializer_list<string> l){
for(auto &p:l){
cout<<p<<endl;
}
}
int main(){
error_msg({"a","b","c"});
return 0;
}
数组作为函数的参数,直接按照数组的格式传递即可,但实际上传递的是数组的指针:
void func(int a[10], int a[3][4]) {
// function body
}
函数的返回值
函数返回值时,是把原来的值复制一遍,单纯地返回值是返回的值的副本。不能返回局部对象的引用或者局部对象的指针,因为局部对象在函数调用结束后就被销毁了。
只有返回引用时,才是返回的左值,其余的情况都是返回的右值。返回引用的一般方式:
char &get_val(string &str, string::string_size ix) {
return str[ix];
}
int main(){
string s("a value");
get_val(s,0)='A';
cout<<s<<endl; // 输出 A value
return 0;
}
也可以返回初始化列表:
vector<string> process() {
return {"A", "B", "C"};
}
C++不支持函数返回数组,所以只能通过返回函数的指针的格式进行。必须记住被定义的名字后面的数组的维数。
int (*func(int i))[10]; // 返回一个大小是10的一维整型数组
上述过程太繁琐,容易出错,在C++11中,可以使用后置声明的方式:
auto func(int i)->int(*)[10] {
}
返回一个指向含有10个整型整数的数组,比如说int arr[3][10]
。C++14中,甚至可以直接使用auto
而不进行后置声明。
使用decltype
用于指向那些明确知道的数组
int odd[] = {1, 3, 5};
int even[] = {0, 2, 4};
decltype(odd) *arrPtr(int i) {
return (i % 2) ? &odd : &even;
}
函数重载
函数重载时,编译器区分同名重载函数的唯一标识是函数的参数,无法通过返回值区分;但是重载可以另返回值不同。
一组正确的重载:
int func(int i) {}
char func(char j) {}
错误的重载,无法仅仅通过返回值区分
int func(int i){}
char func(int j){}
编译器无法区分顶层的const
形参,但是可以区分顶层const
的引用或者指针!!!
错误的重载,无法区分顶层的const
形参
void Foo(const string str) {}
void Foo(string str) {}
void Foo(string*) {}
void Foo(string *const) {}
正确的重载,可以区分引用和指针:
void Foo(const string &str) {}
void Foo(string &str) {}
void Foo(string*) {}
void Foo(const string*) {}
const_cast
的类型转化在参数传递的应用:
string &shorterString(string &s1, string &s2){
auto &r=shorterString(const_cast<const string&>(s1)),
const_cast<const string&>(s2));
return const_cast<string&>(r);
}
在不同的作用域中,无法进行函数重载!
特殊用途语言特性
默认形参必须放在所有的参数最后,或者默认形参的后面所有的参数都是默认形参。可以显式的传入默认形参,但是必须要按照参数的顺序,否则会报错。
内联函数inline
,把函数直接复制到相应的代码处,而不是寻地址调用,适用于较短的、频繁调用的函数体。
constexpr
函数,用于常量表达式的函数,函数的返回值必须是字面值,函数体只能有一个return
。该关键字让编译器在编译时检查返回值是否是常量表达式。
inline
和constexpr
函数都是要放到 头文件中。
函数指针
函数指针用于指向函数,函数指针的类型由返回值和形参共同决定。
定义方式:
bool lengthCompare(const string &, const string &); // 声明一个函数
bool (*pf)(const string &, const string &); // 声明函数指针,pf必须有括号
两种等价的赋值方式:
pf = lengthCompare;
pf = &lengthCompare;
等价的调用方式:
pf('a', 'bc');
(*pf)('a', 'bc')
lengthCompare('a', 'bc');
函数指针可以进行重载,规则参考函数重载,只是把函数名换成了指针而已。
函数指针可以作为形参使用,使用方式:
// 第三个是函数指针的形参
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 string &))
直接使用函数类型过于繁琐,可以使用using
或者typedef
另起别名。
typedef
的方式:
// Func和Func2是函数类型,不是函数指针
typedef bool Func(const string &, const string &);
typedef decltype(lengthCompare) Func2;
// FuncP和FuncP2是函数指针
typedef bool(*FuncP)(const string &, const string &);
typedef decltype(lengthCompare) *FuncP2;
using
的使用方式,更加简介明了,推荐这种方式!
using F = int(int*, int); // 函数类型
using FP = int(*)(int*, int); // 函数指针类型
在函数中,可以使用后置类型推导返回函数指针:
auto f1(int) -> int(*)(int*, int);
假如知道函数的类型,可以使用decltype
进行推导:
int f1(int i, int j);
char f2(char c);
decltype(f1) *getFunc(const string &s); // 注意*,自动退到不会返回指针!