C++Primer学习笔记(5)

这篇笔记的内容是函数。

引入

一、函数基础

1.基本知识

1)编写与调用

2)形参与实参

关于形参实参可参考:形参与实参的区别_百度知道

关于形参实参最精髓的一条就是:形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。

 函数的形参怎么写

这一点是容易疏忽的。

2.局部对象

关键词:作用域、生命周期、局部变量

1)自动对象

2)局部静态对象

 实测,静态局部static变量确实是不会被销毁的。测试代码如下:

size_t cout_calls()
{
	static size_t ctr = 0;
	return ++ctr;
}
int main()
{

    for(int i=0;i<4;i++)
    cout<<cout_calls()<<endl;//依次输出1、2、3、4
}

 3.函数声明

 简单理解就是头文件里为声明,源文件里为定义。关于声明与定义可以看看学习笔记1:C++Primer学习笔记(1)_qq_42987967的博客-CSDN博客

 4.分离式编译

 这一块的内容可太深了,涉及到了编译原理,再往深里想还能想到设计模式中的高内聚低耦合:怎么样能以最小的工作量进行修改。

二、参数传递

C++一般分为引用传递、指针传递、值传递 ,其他语言也类似,如C#包含了值传递以及ref和out关键字,可参考:C# - 函数参数的传递 - DonLiang - 博客园

1.指针形参

2.引用参数

 ​​​​​

 小结一下,使用引用参数即会真正地将传入的实参进行运算的,并且最后的实参值是会变的。

下面讲讲使用引用参数的好处。

1)可避免拷贝

使用引用可以减少拷贝消耗,如果不使用引用的话,对比较大的变量需要整块都拷贝,非常浪费。

 2)可以返回额外信息

3.const形参与实参

 1)指针或引用形参与const

大致解释一下,就是形参不为const的话,那么无法传入const类型的实参的;但是形参为const的话,是可以传入不为const的实参的。例子如下:

	int i = 0;
	const int ci = i;
	void reset(int *p);
	void reset(int& r);
	void reset1(const int& r);
void main()
{
	reset(ci);//报错
	reset(&ci);//报错
	reset(i);//不报错
	reset(&i);//不报错
	reset1(ci);//不报错
	reset1(i);//不报错

}

 2)尽量使用常量引用

4.数组形参 

 

1)使用标记指定数组长度

 这个主要是适用于字符数组。

2)使用标准库规范

3)显式传递一个表示数组大小的形参

 4)数组形参与const

 5)数组引用形参

注意区分有括号

 6)传递多维数组

 同样,注意区分括号

 5.main函数中的参数(处理命令行选项)

 这个估计得用Linux进行C++编程会更清楚些,main函数是有输入参数的,如果是在Windows中可以通过命令行执行exe文件输入命令运行程序,输入的命令即为argv字符数组参数,而argc会根据输入argv个数自动生成。

关于这个,可参考:cmd中运行exe的简单命令_clp786080772的博客-CSDN博客_cmd运行exe文件命令

关于在cmd命令行中输入参数运行VS2017 - 程序员大本营

 6.含可变形参的函数

这部分感觉就比较深了,在这里估计不会介绍的太详细,细节还是得看后面的16.4节课。 

 1)initializer_list形参

 2)省略符形参

 进一步可参考:省略符形参的使用_outsiderJT的博客-CSDN博客_省略符形参

三、返回类型和return语句

 1.无返回值函数

 关于void函数返回一个void函数举个例子:

void test() { cout << "test"<<endl; };
void test1()
{
    return test();//其实还不如不写return
}
int main()
{
    test1();//将打印一个test
    return 0;
}

2.有返回值的函数



 1)值如何返回

比较有趣的是返回引用的函数,之前也算是举过例子了,不过今天再回顾一下吧。

const int& Learn5Function::testconst(const int& r)
{
	return r;
}
void main()
{
	int i = 0;
	const int& a = testconst(i);
	i=2;
	cout << a << endl;//引用a被绑到了i上了
}

 2)不返回局部对象的引用或指针

 3)返回类类型的函数和调用运算符

 4)引用返回左值

 把函数当成左值来用,这个看起来确实是有点奇怪,感觉了解一下就行,不太有用。

5)列表初始化返回值

 6)主函数main的返回值

 7)递归

有用的就最后一句,main函数不能调用它自己。

3.返回数组指针

 这种是取别名的方法。

1)声明一个返回数组指针的函数

 2)使用尾置返回类型

 比较花里胡哨的写法

3)使用decltype

 这也是一种比较复杂的写法了。

四、函数重载

 主要记住两个点,一个是函数名字相同但形参列表不同,一个是main函数不能重载。

1.重载的相关概念

1)定义重载函数

 2)判断两个形参类型是否相异

 3)重载和const形参

 这一块可以结合之前的笔记1:C++Primer学习笔记(1)_qq_42987967的博客-CSDN博客

比较有意思的是上面的最后一点,编译器会优先选用非常量版本的函数。

 4)建议:何时不应该重载函数 

 函数的重载要结合实际情况来,看看到底是重新命名更好理解还是重载更好。

5)const_cast和重载

 这一块没看太懂,有点被绕晕了。

6)调用重载的函数

 大致是在告诉我们编译器是怎么处理重载函数的。

2.重载与作用域

五、特殊用途语言特性

 1.默认实参

 注意最后一点,是后面的形参必须有默认值,不是全部。

1)使用默认实参调用函数

 值得注意的有两点,一个是只能省略尾部实参、一个是让不常用的形参放在前面,常用的尽量放在后面。

2)默认实参声明

3)默认实参初始值

 

 2.内联函数和constexpr函数

1)内联函数可避免函数调用时的开销

内联函数一般都比较短。

 2)constexpr函数

 需要注意的是不能在头文件中定义一个constexpr变量,但是可以定义一个constexpr函数;在头文件中定义的const变量或constexpr函数仍然无法在源文件中去定义一个数组,会报this不能在常量表达式中使用,但是当这些变量或函数只定义在源文件当中则不会出问题。这个可能涉及到了编译原理了,当前能力有限还不能解释为什么

3)把内联函数和constexpr函数放在头文件内

3.调试帮助

 1)assert预处理宏

 2)NDEBUG预处理变量

 关于这两个调试的方法因为不是很熟,平时用的也少,可参考:特殊用途语言特性(默认实参/内联函数/constexpr函数/assert预处理宏/NDEBUG预处理变量) - geloutingyu - 博客园

 assert预处理宏与预处理变量_阳光日志-CSDN博客

六、函数匹配

关键词:形参数量相等以及某些形参的类型可由其他类型转换得来。

1.候选函数、可行函数、函数匹配

 1)确定候选函数和可行函数

 关键是要理解什么是候选函数,什么是可行函数。

2)寻找最佳匹配(如果有的话)

 在可行函数的基础上找到最佳匹配,其满足数据类型能不转换就不转换。

3)含多个形参的函数匹配

 这个讲的就是没有最佳匹配的例子了,因为好几种可行函数都旗鼓相当,这时候就会报二义性的错。

 2.实参类型转换

 告诉我们几种转换的优先级。并且注意区分类型提升和算数类型转换。

1)需要类型提升和算数类型转换的匹配

 2)函数匹配和const实参

七、函数指针

1.函数指针的声明

注意加上括号。

2.使用函数指针

上面最重要的一句话是:当把一个函数当值使用时,函数会自动转换为指针。

 3.重载函数的指针

 4.函数指针形参

 把函数指针当成形参,关于函数指针形参怎么用,可参考:函数指针变量作为函数形参_LC的专栏-CSDN博客_函数指针作为形参

举例说明:(值得注意的是非函数指针形式也可做为形参的)

void a(int i=1){
    cout<<i<<endl;
}
void b(int i=2){
    cout<<i<<endl;
}
void d1(void fun(int i)){//非函数指针形式也可传递
    fun(3);
}
void d2(void (*fun)(int i)){
    fun(4);
}
int main() {
    d1(a);
    d2(b);
    return 0;
}

再整整其他花活,考虑试试弄个函数指针数组(经过个人测试,需要声明成指针形式才可返回)。

void a(int i=1){
    cout<<i<<endl;
}
void b(int i=2){
    cout<<i<<endl;
}

using f=void(int);//一定要用别名,否则也报错
using F = void(*)(int);

int main() {

    f* farry[2];//等价F farry[2],直接f farry[2]则报错
    farry[0]=a;
    farry[1]=b;
    farry[0](1);
    farry[1](2);
    return 0;
}

5.返回指向函数的形参

即告诉我们咋样让一个函数返回一个函数指针,目前很少遇到这种问题。

建议要真遇到了返回函数指针的函数,尽量采用别名,要不然花里胡哨的把自己给整晕壳了都。

void a(int i=1){
    cout<<i<<endl;
}
void b(int i=2){
    cout<<i<<endl;
}

using f=void(int);//f=void(*)(int)
using F = void(*)(int);
f* c(void fun(int i)){//void (*fun)(int i)
    return fun;
}
int main() {
    auto function1=c(a);
    function1(7);
    F function2=c(b);
    function2(9);
    return 0;
}

p.s: 这一块好难,好容易绕晕,记得又多,这或许正是C++内容又臭有多的典例吧。不过大概得记得哪种表达式是什么意思,刚开始不要求能会写,但得该知道查哪一块的内容,做到看别人的代码能看懂。

 6.将auto和decltype用于函数指针类型

7.课后题代码 

这一节的几道课后题不错,代码如下:

## Exercise 6.54
```cpp
int func(int a, int b);

using pFunc1 = decltype(func) *;
typedef decltype(func) *pFunc2;
using pFunc3 = int (*)(int a, int b);
using pFunc4 = int(int a, int b);
typedef int(*pFunc5)(int a, int b);
using pFunc6 = decltype(func);

std::vector<pFunc1> vec1;
std::vector<pFunc2> vec2;
std::vector<pFunc3> vec3;
std::vector<pFunc4*> vec4;
std::vector<pFunc5> vec5;
std::vector<pFunc6*> vec6;
```

## Exercise 6.55
```cpp
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
```

## Exercise 6.56
```cpp
std::vector<decltype(func) *> vec{ add, subtract, multiply, divide };
for (auto f : vec)
          std::cout << f(2, 2) << std::endl;
```
----

see @Mooophy 's [complete codes](ex6_54_55_56.cpp).

有好多种方法,这个我也是抄的,以后看到了可以学习。

最后总结:这一章的内容都比较难,涉及到了大量的指针问题、引用问题、常量问题,这些知识点都是内容又臭有多的,特别是返回数组和返回函数,都必须得用指针,看来以后需要对这一章多看看多复习才是。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值