1.函数使用引用形参返回多个信息
给函数传入一个额外的引用形参来保存其他信息
#include <iostream> #include <string> using namespace std; string::size_type find_char(const string &s, char c, string::size_type &occurs) { auto ret = s.size(); occurs = 0; int i = 0; for(decltype(ret) i = 0; i != s.size(); ++i) { if(s[i] == c) { if(ret == s.size()) //记录第一次出现的位置,只执行一次 ret = i; ++occurs; } } return ret; } int main() { string s; string::size_type occurs = 0; cin >> s; char c; cin >> c; cout << find_char(s, c, occurs) << endl; cout << occurs << endl; return 0; }
2.函数参数为数组时
<1 使用标记指定数组长度
比如数组结束处添加\0..
<2使用标准库规范
void prin(const int *beg, const int *end) { while(beg != end) .... } prin(begin(a), end(a)); //调用
<3 显示的传递一个数组的大小
3.指针或引用形参const
const int *i; //底层,对象是个常量
const int &j;
const int k;//顶层,地址是个常量
顶层const 作为函数的参数会被忽略,
也就是
void func(const int k);//可以传参const 也可以是非const void func2(const int *i); void func3(const int &i); //把const 绑定到非const 上面就错误了 void fun(int k); void fun2(int *k); void fun3(int &k); const int a = 10; fun(a)//不报错,a是副本 fun2(&a);//报错,a是const fun3(a);//报错,a是const4.含有可变形参的函数
如果参数数量不定,但是参数的类型一致
可以使用c++11标准库
initializer_list
头文件#include <initializer_list>
但是注意的一点,initialzer_list里面的元素全是常量。
因为<initializer_list>含有begin 和 end, 所以可以使用范围for
//求和 #include <iostream> #include <initializer_list> using namespace std; int sum(initializer_list<int> li) { int sum = 0; //int &i ,就会报错,initializer_list里面的元素全是const for(const int &i : li) //写成const说明不会改变i , 使用引用避免为副本 { sum += i; } return sum; } int main() { cout << sum({1,2,3,4,5,6,7,8,9,10}) << endl; return 0; }
5.引用返回左值
#include <iostream> using namespace std; int &ret(int *array, int i) { return array[i]; } int main() { int a[10] = {1,2,3,4,5,6,7,8,9,0}; ret(a, 8) = 1000; //给返回值赋值 for(const int &i : a) cout << i << endl; }
返回函数内定义的对象的引用无效,修改返回的常量引用无效。
7.c++11新特性使用尾置返回类型
尾置返回类型放在函数的形参表之后,我们使用auto来代替返回类型
auto func(int i) -> int(*)[10]
或者我们知道函数返回的指针指向哪个对象,就可以使用decltype关键字声明返回类型
但是decltype并不负责把数组类型转换成对应指针,必须自己添加
#include <iostream> using namespace std; string (* arr(void)) [10] { static string a[10] = {"1","2","3","4","5","6","7","8","9","10"}; return &a; } int (* arr2(void))[10] { static int a[10] = {1,2,3,4,5,6,7,8,9,0}; return &a; } string (& arr3(string s))[10]; int main() { }
分别用类型别名,尾置返回类型,decltype来改写一个返回数组的函数
#include <iostream> using namespace std; using arr = string[10]; string (& arr1(string a[10]))[10]; //1.使用类型别名 arr* arr2(string a[10]); //使用尾置返回类型 auto arr3(string a[10])->string(*)[10]; //使用decltype string b[10]; decltype(b)* arr4(string a[10]); int main() { return 0; }
8.const_cast 和函数重载
这个在我的另外一篇文章中也有写到。
在非const 里面调用const 版本然后去除const 的特性。
#include <iostream> using namespace std; const string& shortString(const string &s1, const string &s2) { cout << "调用 const string &" << endl; return s1.size() < s2.size() ? s1 : s2; } string& shortString(string &s1, string &s2) { cout << "调用 string &" << endl; auto &s = shortString(const_cast<const string&>(s1), const_cast<const string&>(s2)); return const_cast<string &>(s); } int main() { const string s1 = "hello"; const string s2 = "world"; string s3,s4; cin >> s3 >> s4; shortString(s1, s2); shortString(s3, s4); return 0; }
9.默认实参
#include <iostream> #include <string> using namespace std; typedef string::size_type st; void scree(st hi = 20, st wi = 20, char back = 'a') { cout << hi << " " << wi << " " << back << endl; } int main() { scree(3, 3, 'a'); scree(); }注意:一但某个形参被赋于了默认值,它后面所有的形参都必须有默认值
char * init(int ht = 24, int wd, char bakg); //error
调用默认实参,省略参数就行。
尽量让不太使用的实参出现在函数参数表后面
10.对于那些经常使用的函数应该指定为内敛函数
inline关键字
内敛函数可避免函数调用的开销
内敛机制用于优化规模小,流程直接,频繁调用的函数
11.constexpr修饰函数, 用于返回常量表达式
constexprt函数体内也可以有空语句,只要这些语句在运行时不执行任何操作就行。
#include <iostream> #include <string> using namespace std; constexpr const string& shortString(const string &s1, const string &s2) { //return s1.size() <= s2.size() ? s1 : s2; error return s1; } int main() { const string s1 = "hello"; const string s2 = "world"; //constexpr string s3 = "aasd"; error cout << shortString(s1, s2) << endl; return EXIT_SUCCESS; }
12.调试帮助
assert和NDEBUG
assert(条件) 条件为真什么也不做,条件为假发出错误并打印错误
assert的行为依赖于NDEBUG预处理变量的状态。 如果定义了NDEBUG 那么assert什么也不做
定义NDEBUG能避免检查各种条件所需的运行时的开销
也可以自己使用NDEBUG编写调试代码
#include <iostream> //#define NDEBUG //注意位置,NDEBUG必须写在assert.h的前面,否则不起作用 #include <assert.h> void fun(int a = 10, int b = 20) { assert(a == 9); std::cout << a << " " << b << std::endl; std::cout << __FILE__ << std::endl; std::cout << __LINE__ << std::endl; std::cout << __TIME__ << std::endl; std::cout << __DATE__ << std::endl; } int main() { fun(); return 0; }有#define NDEBUG 和 没有的运行结果
__FILE__存放文件名的字符串字面值
__LINE__存放当前行号的整型字面值
__TIME__存放文件编译时间的字符串字面值
__DATE__存放文件编译日期的字符串字面值
13.函数指针
#include <iostream> using namespace std; void func(int a, int b) { cout << a << " " << b << endl; } int main() { void (*p) (int a, int b); p = func; p(1,3); (*p)(1,3); }函数指针定义一般显得非常繁琐
所以我们可以使用typdef 和 decltype来简化
typedef bool Func(const string&, const string&); typedef decltype(Func) *Funcp; //decltype要自己加上*,它不会自动转换成指针类型
返回指向函数的指针
要想声明一个返回函数指针的函数,最简单的方法是使用类型别名
using F = int(int *, int); //函数类型 using FF = int(*p)(int *, int); //函数指针类型 FF f1(int); F* f2(int); int(*f1(int)) (int *, int); //也可以直接声明,f1是个有参列表,且有*说明是个指针,指针指向一个函数 auto f1(int) -> int(*)(int, int);//尾置返回类型
auto和decltype作用于函数指针类型
#include <iostream> #include <string> using namespace std; int fun1(int a) { int sum = 1; while(a-- != 0) { sum *= a; } return sum; } decltype(fun1)* fun3(void) { cout << "调用fun1" << endl; } int main() { fun3(); return 0; }