C++Primer学习——函数
编译器能以任意顺序对形参进行求值
函数的返回类型不能是数组类型和函数类型。
函数开始时为形参分配内存,一旦函数结束,形参也就被销毁了。
如果弄成静态局部变量,那么回到程序终止结束时才被销毁。
void fo()
{
static int a ; //只在第一次初始化
a++; //保存了前次被调用后留下的值
return ;
}
//所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量
形参类型决定了形参与实参的交互方式
f(int a,int b) 被调用时可以看成 f(int a = x,int b = y);
f(int &a,int &b) 被调用时可以看成 f(int &a = x,int &b = y);
使用引用避免拷贝:
拷贝大的类类型或者容器的对象比较低效,甚至有的类类型不支持拷贝操作
参数的const:
顶层const会被忽略,所以两个fcn的参数是一样的(顶层const没法区分参数)
void fcn(const int a){}
void fcn(int a){}
尽可能使用常量引用:
void find(string &s){
}
void find_char(const string &s){
}
find("hhh"); //error 普通引用不能引用字面值
find_char("hhh"); //ok
但底层const看成不同的参数(作用于不同的对象 常量or非常量)
编译器可以通过实参是否是常量来判断调用哪一个(而且非常量优先选择非常量版本)
Record look(Account *)
Record look(const Account *)
Record look(Account &)
Record look(const Account &)
含可变参数:
Initializer_list:
initializer_list是C++标准程序库中的一个头文件,定义了C++标准中一个非常轻量级的表示初始化器列表的类模板initializer_list及有关函数。
与vector不同的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。
initializer_list lis(ls);
initializer_list lis = ls; //原始列表和副本共享元素
C++11允许构造函数和其他函数把初始化列表当做参数
void fcn(initializer_list<int> ls)
{
for(auto i : ls)
{
cout << i <<" ";
}
cout << endl;
}
int main()
{
fcn({1,2,3});
return 0;
}
class node
{
public:
node(int a,int b){}
node(initializer_list<int>) {}
node(){};
};
int main()
{
node a;
node b(1,2);
node c{2,3,4,5,6};
node d = {1,3,4,5,6};
return 0;
}
省略符类型:
只是用与C/C++通用类型,大多数类类型在传递省略符形参时都无法正确拷贝
void foo(int a,...)
void foo(...)
返回值:
返回值的方式和初始化类似,返回值会被拷贝到函数的调用点用于初始化,如果返回引用又是另一回事。
而且别返回局部变量指针或者引用,因为函数结束一会后其所占空间会被释放掉
int& fo(int a)
{
int b = a;
cout << &b <<endl;
return b;
}
int main()
{
int &a = fo(100); //a,b地址相同,说明并没有经过拷贝
cout << &a <<endl;
cout << a <<endl;
}
函数的返回类型决定返回是否为左值:
当函数返回引用的时候所得到的是左值,其它所得到的都是右值。
char &to_Get(string& a,int index)
{
return a[index];
}
int main()
{
string ta("abcd");
cout << ta <<endl;
to_Get(ta,1) = 'B';
cout << ta << endl;
/*
abcd
aBcd
*/
}
返回数组指针:
数组因为不能被拷贝,所以函数不能返回数组。可以返回数组的引用和指针,只是比较麻烦.
①.利用别名简化。
typedef int arr[10];
using arr = int[10];
arr* func(int i);
②.声明一个返回指针的函数(从内往外读)
int (*a)[10];
int (*func(int a,))[10]; //格式相似
③.尾置返回类型
在->符号后面指明函数的真正返回类型
auto func(int a) -> int(*)[10]
{
}
④.使用decltype(但是会返回数组类型,而数组又无法赋值,所以搞成指针)
int odd[] = {1,3,5,7};
decltype(odd)* func()
{
return &odd; //返回数组类型的指
}
int main()
{
auto p = func();
cout << p << " " <<odd <<endl;
}
重载:
函数重载:应该在函数参数个数或者形参类型上面有所不同.
如果在当前作用域中找到了所需的函数名,那么编译器会自动忽略外层作用域的同名实体。(不同作用域不能重载函数名)
void Print(string a);
void Print(double a);
void f(int a)
{
void Print(int);
print("abc"); //error:与int不符
Print(2.5); //输出了int
}
void Print(double a){ cout << "double " << endl;}
void Print(int a){cout << "int" << endl;}
void Print(string a){cout << "string" << endl;}
默认实参:
一旦某个形参被赋予了默认实参,那后面全部都要有默认值。 而且函数调用时,只能省略尾部实参
windows = screen(,,'s'); //error
windows = screen('s');
函数可以被声明多次,但是一个形参只能被赋予一个默认值。
string screen(sz,sz,char = ' ');
string screen(sz,sz,char = '?'); //error,重复声明
string screen(sz=24,sz=80,char); //correct,添加默认实参
(局部变量不能作为默认实参)
int pa = 100;
void fo(int a = pa); //默然实参为pa
void f()
{
int pa = 80; //隐藏了外部的pa
fo(); //没改变默认值
}
int main()
{
f();
}
void fo(int a)
{
cout << a <<endl;
}
内联函数:
调用函数比一般的求表达式的值要慢一点,大多数机器上,一次函数调用包含着很多工作:调用前先保存寄存器,并在返回时恢复;
可能需要拷贝实参;程序转下一个新的位置继续执行。
内联函数(优化规模小,流程直接,调用频繁的函数; 有的编译器不支持):
cout<<shorterString(s1,s2)<<endl;
//在编译器看来会转换成
cout << (s1.size() <= s2.size() ? s1:s2) <<endl;
constexpr函数:
编译器把对constexpr函数的调用替换成它的结果了,被隐式地指定为内联函数。
允许返回值并非一个常量?
A (non-template) constexpr function must have at least one execution path that returns a constant expression
//必需要能返回至少一个常量表达式
constexpr int f(bool b) { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
template<bool B> constexpr int g() { return f(B); } // OK
constexpr int h() { return g<true>(); } // ill-formed, no diagnostic required
http://stackoverflow.com/questions/31206937/a-constexpr-function-is-not-required-to-return-a-constant-expression\
但如果把函数用在需要常量表达式的上下问时,则编译器会检测函数结果是否符合要求
内联函数和constexpr函数可以在程序中被定义多少,通常被定义在头文件
assert:
一种预处理宏,行为类似内联函数。 assert(expr)//cassert头文件,由预处理器管理,不需要命名空间
assert(word.size() > threshold);
如果定了NDEBUG预处理变量,那么assert什么也不做
参数匹配:
1.与被调用函数同名 2.其声明在调用点可见
3.形参数量相同 4.实参和形参的类型相同,或者能够转换
然后从可行的当中找出最佳匹配
匹配成功:①每个实参的匹配都不劣于其它可行函数的匹配
②至少有一个实参匹配优于其它
否则会产生二义性
void f(int a,int b)
void f(double a,double b)
int main()
{
f(3,2.15);
}
上面这个例子,两个函数中找不到一个脱颖而出的。 所以会产生二义性。
//要避免强制类型转化,出现只能说明我们的形参集合设置的不合理
匹配等级:
①精确匹配:
1.实参和形参的类型相同
2.从数组类型或者函数类型转换它们的对应的指针类型
3.想实参中添加顶层const或者删除顶层const
//2016年11月20日 22:00:24
②通过const转换 (添加底层const:const&什么的)
③通过类型提升 (小类型提升为大类型)
④通过算术类型转换或者指针转换 (运算符的运算对象转换成最宽;0或NULL转换成任意指针,任意非常量对象指针->void,任意对象->const void)
⑤类类型转换
void f(float x);
void f(int x);
f(3.45); //double型,而所有算术转换等级相同,产生二义性
void f(int a);
void f(short b);
f('a'); //类型提升,但是会提升为较大范围的int。只有类型是short时才会调用short版本
函数指针:
函数的类型是由它的返回类型和行参类型决定的。 函数可以自动地转换成指针,指针可以调用该函数
bool lengthCompare(const string&,const string &);
bool (*p)(const string&,const string&);
p = lengthCompare;
p = &lengthCompare; //等效,函数名会转换成指针
bool t = p("hello","bye");
bool t = (*p)("hello","bye"); //等效
对于重载函数,编译器会通过指针类型选哪个函数,指针类型必需与重载函数的某一个精确匹配.
函数指针可以作为一个函数的形参。
简化函数指针代码 decltype and typedef:
typedef bool func(const string&,const string&); //函数
typedef bool (*func)(const string&,const string&); //函数指针
typedef decltype(lengthCompare) func2;
typedef decltype(lengthCompare) *func2;
decltype()会返回一个函数类型而且不会自动转换成指针
返回函数的指针:
using p = int(int*,int );
using tp = int(*)(int*,int );
tp f1(int); //correct tp是指向函数的指针
p f1(int); //error p是函数类型,不能返回一个函数
p* f1(int); //correct
int (*f1(int*,int))(int*,int ); //直接声明一个返回函数指针的函数
首先f1前面*,所以f1返回一个指针;指针类型包含形参列表,所以指针指向一个函数
尾置型:
auto f1(int) -> int (*)(int*,int);
C++Primer学习——函数的更多相关文章
- C++ Primer学习笔记(三) C++中函数是一种类型!!!
C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- Matlab中常见的神经网络训练函数和学习函数
一.训练函数 1.traingd Name:Gradient descent backpropagation (梯度下降反向传播算法 ) Description:triangd is a networ ...
- C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象
重载操作符与转换 --调用操作符和函数对象 引言: 能够为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符! struct absInt { int operator() (int v ...
- C++ primer学习笔记_6_函数---函数定义、参数传递
1. 习题参考: 6.14 举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子. 答: 形参使用引用类型的情况:(1)避免拷贝传递大对象的时候,这里的string对象s:(2)当需要从函 ...
- 转载:看c++ primer 学习心得
学习C++ Primer时遇到的问题及解释 chenm91 感觉: l 啰嗦有时会掩盖主题:这本书确实有些啰嗦,比如在讲函数重载的时候,讲了太长一大段(有两节是打了*号的,看还是不看 ...
- JavaScript基础学习-函数及作用域
函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...
- 05- Shell脚本学习--函数
函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高.像其他编程语言一样,Shell 也支持函数.Shell 函数必须先定义后使用. 函数定义 Shell 函数的定义格式 ...
- .Net程序员之Python基础教程学习----函数和异常处理[Fifth Day]
今天主要记录,Python中函数的使用以及异常处理. 一.函数: 1.函数的创建以及调用. def Add(val1,val2): return val1+val2; print Add( ...
随机推荐
- C语言第二次作业
一.PTA实验作业 题目1:7-1 计算分段函数[2] 1.实验代码 double x,y; scanf("%lf",&x); if (x>=0) { y=sqrt( ...
- 201621123060《JAVA程序设计》第十周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1. 常用异常 结合题集题目7-1回答 1.1 自己以前编写的代码中经常出现 ...
- Linux下进程间通信的六种机制详解
linux下进程间通信的几种主要手段: 1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具 ...
- Flask 学习 十 博客文章
提交和显示博客文章 app/models.py 文章模型 class Post(db.Model): __tablename__ = 'posts' id = db.Column(db.Integer ...
- vue style width a href动态拼接问题 ?
style width 这个问题 折磨了我一个上午了 好惭愧 因为项目涉及到 进度条 所以必须用行内样式 style 用过vue的都知道 vue中style的用法 大众用法 :style=&quo ...
- 一个诚实的孩纸选Python的原因
我之所以会选择python语言程序设计这门课,是因为我一开始预选选的选修课都没选上,然后在补选的时候,在别人选剩的课里面选择了python. 上了两节课之后,我发现python还挺有意思的,挺喜欢py ...
- js 中bind
function fn(a){ this.innerHTML = a; console.log(this); } //fn("hello"); span1.onclick =fun ...
- vue-入门
数据绑定 <!--步骤1:创建html文件--> <!DOCTYPE html> <html lang="en"> <head> ...
- 测试驱动开发实践5————testSave之修改文档分类
[内容指引] 1.确定"修改文档分类"的微服务接口及所需的参数 2.设计测试用例及测试用例合并 3.为测试用例赋值并驱动开发 上一篇我们通过17个测试用例完成了"新增文档 ...
- JavaScript正则表达式学习笔记之一 - 理论基础
自从年前得空写了两篇文章之后就开始忙了,这一忙就是2个月