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( ...
随机推荐
- 201621123040《Java程序设计》第5周学习总结
1.本周学习总结 1.1写出你认为本周学习中比较重要的知识点关键词 关键词:接口 Comparable Comparator 比较排序 1.2尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需 ...
- 小黄衫 Get
小黄衫 Get . 十分荣幸在前四次作业中以微弱的3分之差拿到了第一,获得了本次的小黄衫. 先发点牢骚.. 讲道理,原本以为的研究生生涯应该就是埋在论文堆里度过的时候顺便上上课.当初选课的时候,学 ...
- Scapy实现SYN泛洪攻击
一.实验说明 1.实验介绍 本次实验将使用python3版本的Scapy--Scapy3k来实现一个简单的DDos,本次实验分为两节,本节将学习如何使用Scapy3k来实现SYN泛洪攻击. 2.知识点 ...
- LR回放https协议脚本失败: 错误 -27778: 在尝试与主机“www.baidu.com”connect 时发生 SSL 协议错误
今天用LR录制脚本协议为https协议,回放脚本时出现报错: Action.c(14): 错误 -27778: 在尝试与主机"www.baidu.com"connect 时发生 S ...
- vue 中获取select 的option的value 直接click?
我刚开始遇到这个问题的时候 直接用的click进行dom操作获取value 但是发现并灭有什么作用 问了旁边大神 才想起来还有change这个操作 于是乎 答案有了解决方案 1.在你的select中添 ...
- php网上支付易宝
巴巴运动网是通过易宝向招商银行打钱,这个首先易宝是需要审核巴巴运动网的钱来的是否正当不然易宝就成了一个洗钱的工具,这个是犯法的:因为钱的来路不明!财政部是需要抓起来的!所以钱的流向实际上是用户的招商银 ...
- 如何用UPA优化性能?先读懂这份报告!
一.概述 打开一份UPA报告时,最先看到的就是概述页面,这也是我们推荐用户第一时间关注的页面.概述页面一开始会列出测试的基本信息,并根据腾讯游戏的性能标准,给出本次测试的结果(通过,不通过和警告): ...
- Python内置函数(14)——bytes
英文文档: class bytes([source[, encoding[, errors]]]) Return a new "bytes" object, which is an ...
- Django ORM那些相关操作
一般操作 https://docs.djangoproject.com/en/1.11/ref/models/querysets/ 官网文档 常用的操作 <1> all() ...
- Python之递归函数
递归函数 初识递归函数 递归函数的定义:在一个函数里再调用这个函数本身 Python为了考虑保护内存占用情况,有一个递归深度的限制. 探究递归的默认最大深度: def foo(n): print(n) ...