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是const
4.含有可变形参的函数
如果参数数量不定,但是参数的类型一致
可以使用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;
}
6.什么情况下返回的引用无效,什么情况下返回的常量引用无效
返回函数内定义的对象的引用无效,修改返回的常量引用无效。
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;
}
注释的都是有错误的,constexpr函数不能在调用时计算,运行时不能执行任何操作。
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;
}