第六章函数知识点比较多
知识点:
1. 使用类型别名定义数组指针:typedef int arry[10]; arry 相当于 含有10个int型的一个数组;
2. int (* func(int i)) [10] 由内向外,从最里面的变量开始; *fun (int i) 返回一个指针, 对该指针指向一个int [10] 有10个元素的数组。在C++11里,可以跟简便些,auto fun(int i)->int (*) [10]。
3. 对于重载函数,它们应该在形参数量或形参类型上有所不同。不允许两个函数除了返回类型外其他所有的要素都相同。形参的名字是可以省略的,形参的名字仅仅是帮助记忆,有没有它并不影响形参列表的内容。
4. const和函数重载关系,这一点是自己看的是个人理解的:之前对顶层const与底层const,大致了解他们的意思,这里在总结一下:顶层const修饰的是对象本身,一旦使用顶层const,该对象的内容不能改变。如:int const= 5;而对于底层const ,一般可以通过指针,引用是说内容是const。
5. 顶层const不影响传入的函数对象,一个拥有顶层const的形参无法和另一个没有的形参区分开来。
6.如果形参是某种类型的指针或引用,则通过区分指向的是常量对象还是非常量对象可以实现函数重载。
7. 将函数声明置于局部作用域,不是很明智的选择。
8. 当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么使用默认值的形参出现在前面, 而让那些经常使用默认值的形参出现在后面。
9. 局部变量在函数声明或者定义时,不能做默认实参(测试时调用可以),只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认参数。还要注意,在给定的作用域中一个形参只能被赋予一次默认实参。PS:这个规则不太懂,出发点是什么?
10.关于constexpr修饰的函数: constexpr修饰的函数,返回值不一定是编译期常量。如果其传入的参数可以在编译期间计算出来,那么这个函数就会产生编译时期的值。但是,传入的参数如果不能再编译期间计算出来,那么constxpr修饰的函数就和普通函数一样了。注:检测constexpr函数是否产生编译时期值的方法利用数组的维度需要编译期常值来确定。
11. constexpr表示这玩意儿在编译期就可以算出来(前提是算出它所依赖的东西也是在编译期可以算出来的)。而const只保证了运行时不直接被修改(但这个东西仍然可能是个动态变量)。
12.预处理宏assert(expr):包含一个表达式,expr为真时,assert什么也不做,为假时输出信息并终止程序。包含在cassert头文件中。通常用于检查不能发生的条件。 2:assert依赖于一个NDEBUG的预处理变量的状态,如果定义了NDEBUG,assert什么也不做,默认状态下NDEBUG是未定义的。编译器也可以预先定义该变量。3:也可以使用NDEBUG编写自己的条件调试代码,如果NDEBUG未定义,将执行#ifndef到#endif之间的代码,如果定义了NDEBUG,这些代码将被忽略。
13.设计重载函数时,要避免二义性。
14.函数指针指向的是函数而非对象,函数指针指向的函数类型由函数的返回类型和形参决定。声明一个函数指针只需将指针替代函数名即可。但是要注意加()。
bool (*pf) (const string &, const string &)//pf指向一个函数返回值类型为bool,此时还未初始化。pf两端的括号必不可少,若无括号,pf为一个返回值为bool指针的函数。
15.当我们把函数名作为一个值使用时,该函数自动转换成指针。也就是说给函数名取地址符是无关紧要,是可选的。同时,我们可以直接使用指向函数的指针调用该函数,无需解引用。
16.和数组类似,函数不能定义成形参,但是形参可以是指向函数的指针,虽然看起来像是函数类型,实际上是当成指针使用。
习题:
6.31 答:局部对象返回引用无效。
6.32 答:合法,arry虽然是个局部指针,但是 return arry[index],返回的是之前就从在数组元素,相当于*(arry+index),在get函数被销毁后,数组内的元素依然存在。
6.34 答:如果传入的实参是负数,将会死循环,耗尽内存。
6.35 答:这道题值得仔细琢磨一下,如果ival-1改成ival--;则在传参的时候,ival会被传给fun6_35(int ival),而ival总是先传参数,后减一,这就导致了ival一直是开始传参的那个数,会造成死循环。
int fun6_35(int ival)
{
if(ival>0)
return fun6_35(ival-1)*ival;
return 1;
}
6.36 答:注意括号,先从内部函数写起,向外扩展。
string &(fun6_36(string (&str)[10]))[10];//若果传参不考虑拷贝的话,注意(&str)的符号
6.37 答 :
a: typedef string Str [10]; Str & fun6_36(Str &s);
b: auto fun6_36(string (&str))-> string (&) [10];
c: Str ss [10]; decltype( ss) &fun6_36( Str &s ));
6.38 答:
using arrStr = int [10];
decltype(arrStr)& arrPtr(int i)
{
return (i % 2) ? odd : even;
6.39 答:(a) 错误。声明一个名为calc,返回类型为int,形参类型是cosnt int 类型的函数,但是无法重载,因为是顶层const。
(b) 错误。无参的返回类型为double的get函数,但是也是无法重载,int与double转换。
(c) 正确。声明一个名为reset,返回类型为double * ,形参类型也是double *的函数。
6.40 答:(a) 正确。 (b)错误,调用时无法给后面的参数赋值。
6.41 答:(a) 非法,第一个参数需要赋值; (b) 正确;(c) 错误,覆盖默认参数时,类型不匹配。
6.43 答:(a)内联函数可以放置于头文件中,(b)函数声明 也放置于头文件中。
6.45 答:内联的机制用于优化规模较小,流程直接,频繁调用的函数,函数前+inline只是想编译器建议,编译器可以决定不采用。
6.46 答: 不可以的。其参数类型为string类型,不是字面值类型。
6.47 答: 经测试发现NDEBUG在release下不起作用。
6.48 答:while 判断后过的cin的状态是正确的, 所以assert在这里没有意义。
6.49 答:候选函数:函数匹配的第一步是选定本次调用的重载函数集。可行函数:符合并且可以执行的函数。
6.50 答:(a) 非法 2.56匹配double,42匹配int; (b) 合法 f(int) (c) 合法 匹配f(int ,int) (d)合法 匹配f(double double)
6.52 答:(a) 函数重载 (b) 函数重载 (c) 无法形成函数重载,存在顶层const 。
6.54 答: 繁琐的声明,最好用typedef或using 替代
using pf = decltype(func) *;
using pf1 = int(*) (int a, int b);
using pf2 = int (int a, int b);
typedef int (*pf3) (int a, int b);
typedef decltype (func) *pf4;
vector <pf> pf;
vector <pf1> pf1;
vector <pf2*> pf2;
vector <pf3> pf3;
vector <pf4> pf4;
6.55 答:
程序题:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//递归输出vector对象, 确定好边界是关键
int fun6_33(vector <char> vec1,int num)
{
if(num == 0)
return 0;
cout<<vec1[--num];
vec1.pop_back();
fun6_33(vec1,num);
}
int fun6_35(int ival)
{
if(ival>0)
return fun6_35(ival-1)*ival;
return 1;
}
//如果计数值大于1,返回复数类型
string fun6_42make_plural(size_t ctr, const string& word, const char& ending = 's')
{
return (ctr > 1) ? word + ending : word;
}
//NDEBUG下测试
void fun6_47(vector <char> vec1, int num)
{
#ifndef NDEBUG;
cout << "vector size: " << vec1.size() << endl;
#endif
if(num==0)
return ;
auto st = vec1[--num];
vec1.pop_back();
fun6_47(vec1,num);
cout<<st<<" ";
}
//fun6_51 是为区别重载函数的差别之处
void fun6_51()
{
cout<<"f()"<<endl;
}
void fun6_51(int i)
{
cout<<"f("<<i<<")"<<endl;
}
void fun6_51(int i, int j)
{
cout<<"f("<<i<<','<<j<<")"<<endl;
}
void fun6_51(double i, double j)
{
cout<<"f("<<i<<','<<j<<")"<<endl;
}
//加法运算
int fun6_55add( int a, int b)
{
cout<<"+ ";
return a+b;
}
//减法运算
int fun6_55sub( int a, int b)
{
cout<<"- ";
return a-b;
}
//乘法运算
int fun6_55mul( int a, int b)
{
cout<<"* ";
return a*b;
}
//除法运算
int fun6_55div( int a, int b)
{
cout<<"/ ";
return a/b;
}
int main(int argc, char * argv[])
{
/*
vector <char> vec(26);
for(int i=0; i<26; ++i)
{
vec[i] = static_cast<char>(i+65);
}
fun6_33(vec,26); //这里是26匹配上面vec1[--num]; 而25匹配vec1[num--]
//fun6_35(5);
//6_42 题目阐述没看明白
cout<<"succeess的单复数"<<endl<<fun6_42make_plural(1,"success")
<<" "<<fun6_42make_plural(2,"success");
fun6_47(vec,26);
fun6_51(2.52,42); 有多个重载函数,无法匹配
fun6_51(42);
fun6_51(42,0);
fun6_51(2.56,3.14);
*/
typedef int (*pf) (int,int);
vector <pf> vec1;
vec1.push_back(fun6_55add);
vec1.push_back(fun6_55sub);
vec1.push_back(fun6_55mul);
vec1.push_back(fun6_55div);
//6.55,6.56 一起
for(int i=0; i<4; ++i)
{
cout<<vec1[i](10,1)<<endl; //知道函数地址,按照其定义形式传参访问。
}
return 0;
}