1、函数基础

  典型的函数定义包括:返回类型、函数名、由0个或多个形参组成的列表以及函数体。

2、参数传递

  形参初始化的机理和变量初始化一样。

  有两种方式:引用传递和值传递

2.1 传值参数

  当形参是非引用类型时,形参初始化和变量初始化一样,将实参的值拷贝给形参。

指针形参

  当执行指针拷贝操作时,拷贝的是指针的值,拷贝之后,两个指针是不同的指针。但通过指针可以修改它所指的对象。

2.2 传引用参数

使用引用避免拷贝

  拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型根本就不支持拷贝操作。当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。

使用引用形参返回额外信息

2.3 const形参和实参

  当用实参初始化const形参时会忽略顶层const。因此,当形参有顶层const时,传给它常量对象或者非常量对象都是可以的。

void fun1(const int i){.......}

void fun2(int i){.....}

  上述两个函数不能算是重载,两个函数是一样的,程序会报错,fun2重复定义了fun1.

指针或引用形参与const

  可以使用非常量初始化一个底层const对象,但是反过来不行。同时一个普通的引用必须用同类型的对象初始化。

尽量使用常量引用

2.4 数组形参

数组有两个重要的特性:

  • 不允许拷贝
  • 使用数组时会转换成指针

尽管不能以值传递的方式传递数组,但是可以将形参写成类似数组的形式

void print(const int*);
void print(const int[]);
void print(const int[10]); 以上三种形式的声明等价

NOTE:当函数不需要对数组元素执行写操作的时候,数组形参应该是指向const的指针。只有当函数确实要改变元素值的时候,才把形参定义成指向非常量的指针。

当数组作为函数形参时,因此应该提供一些额外信息来确定数组的确切尺寸,管理数组形参有三种常用的技术:

使用标记指定数组长度

  要求数组本身包含一个结束标记。例如C风格字符串以空字符结尾。

使用标准库规范

  传递指向数组首元素和尾元素的指针。

void print(const int *beg,const int *end)
{
while(beg!=end)
{
cout<<*beg++<<endl;
}
}
int arr[2]={0,1};
print(begin(arr),end(arr));

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

  专门定义一个表示数组大小的形参。

void print(const int ia[], size_t size);

int j[]={0,1};

print(j, end(j)-begin(j));

数组引用形参

  形参可以是数组的引用,此时,引用形参绑定到对应的实参上,也就是绑定到数组上。

void print(int (&arr)[10])
{
for(auto elem:arr)
{
cout<<elem<<endl;
}
}
形参是数组的引用,维度是类型的一部分

NOTE:arr两端的括号必不可少

f(int &arr[10]);    //错误,将arr声明成了引用的数组
f(int (&arr)[10]); //正确,arr是具有10个整数的整型数组的引用

传递多维数组

  数组第二维的大小都是数组类型的一部分,不能省略。传递多维数组传递的是指向数组的指针,实际还是指向首元素的指针。(多维数组就是数组的数组,数组的首元素还是数组,所以是指向数组的指针)。

void print(int (*matrix)[10],int size);  matrix是一个指针,指向有10个整数的数组

也可以用:

void print(int matrix[][10],int size);  matrix和上面一样的意义

2.5 含有可变形参的函数

C++提供两种方法:

实参类型相同,可以传递一个名为initializer_list的标准库类型

initializer_list形参

lnitializer_list和vector一样都是模板类型,不同的是initializer_list对象中的元素永远是常量值,不能改变。

void error_msg(initializer_list<string> ls)
{
for (auto beg = ls.begin(); beg != ls.end(); ++beg)
{
cout << *beg << " ";
}
cout << endl;
}

error_msg({ "hello" });
error_msg({ "hello!", "world!!" });  //注意值的传递要放在花括号里

省略符形参

  省略符形参是为了便于C++程序访问某些特殊的C代码而设置的。通常,省略符形参不应用于其他目的。省略符形参只能出现在形参列表的最后一个位置。

实参类型不同,使用可变参数模板

3、返回类型和return语句

3.1 无返回值函数

  返回类型是void类型的函数

3.2 有返回值函数

值是如何被返回的

  返回一个值的方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。

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

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

auto sz=getstring().size();    //getstring返回的string对象再调用size函数

引用返回左值

调用一个返回引用的函数得到左值,其他返回类型得到右值。

列表初始化返回值

函数可以返回花括号包围的值的列表。

vector<string>  process()
{
return {"ni","hao"};
}

递归

如果一个函数调用了它自身,不管这种调用是直接还是间接的,都称该函数为递归函数。

int factorial(int val)
{
if(val>1)
return factorial(val-1)*val;
return 1;
}
求1x2x3x4......

3.3 返回数组指针

因为数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用。

最直接的方法是使用类型别名

typedef int arrT[10];
using arrT=int[10];

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

int arr[10];    //arr是一个含有10个整数的数组
int *p1[10]; //p1是一个含有10个整型指针的数组
int (*p2)[10]=&arr; //p2是一个指针,其指向一个有10个整数的数组

如果要定义一个返回数组指针的函数,则数组的维度必须跟在函数名字之后,并且函数的形参列表应该先于数组的维度。

int (*func(int a,int b))[10];

此函数返回的是一个指向有10个整数数组的指针。

使用尾置返回类型

任何函数的定义都能使用尾置返回,但是这种形式对于返回类型比较复杂的函数最有效,比如返回类型是数组的指针或引用。

尾置返回类型跟在形参列表后面并以一个->符号开头。为了表示函数真正的返回类型跟在形参列表之后,我们在本应该出现返回类型的地方放置一个auto。

auto func(int i)->int (*)[10];

使用decltype

4、函数重载

如果同一作用域内的几个函数名字相同但形参列表不同,称为函数重载。注意必须是形参列表不同,仅仅只是返回类型不同不可以称为重载。

重载和const形参

顶层const不影响传入函数的对象。一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。

int f1(int i);
int f1(const int i); //不构成重载,重复声明了f1 int f2(int *i);
int f2(int *const i); //不构成重载,重复声明了f2

但底层const不同,可以构成重载

int f1(int &i);
int f1(const int &i); //重载,新函数 int f2(int *i);
int f2(const int *i); //重载,新函数

NOTE:最好只重载那些确实非常相似的操作。

const_cast和重载

const_cast在重载函数的情景中最有用。

const string &shorterString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
} string &shorterString(string &s1, string &s2)
{
auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2)); return const_cast<string&>(r);
}

4.1 重载与作用域

如果在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体。

void func()
{
} int main()
{
int func=0;
func(); //错误,此时func是int类型的变量,不是函数,隐藏了外层的函数定义
return 0;
}

5、特殊用途语言特性

  默认实参、内联函数和constexpr函数。

5.1 默认实参

一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。

string  screen(int i=10, int a=1, stirng s=" ");

使用默认实参调用函数

在调用函数的时候省略该实参就可以。

默认实参声明

在给定的作用域中一个形参只能被赋予一次默认实参。

默认实参初始值

局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参。

5.2 内联函数和constexpr函数

调用函数一般比求等价表达式的值要慢一些。

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

在函数的返回类型前面加上关键字inline。

一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。

constexpr函数

constexpr函数是指能用于常量表达式的函数。

定义constexpr函数要遵循:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。

constexpr int new_sz()
{
return 42;
} constexpr int foo=new_sz(); //foo是一个常量表达式

为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。

NOTE:constexpr函数不一定返回常量表达式。

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

和其他函数不一样,内联函数和constexpr函数可以在程序中多次定义。不过,对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。因此,内联函数和constexpr函数通常定义在头文件中。

5.3 调试帮助

两项预处理功能:assert和NDEBUG

assert预处理宏

assert宏常用于检查“不能发生”的条件。

assert(expr);

如果expr为假,assert输出信息并终止程序执行,如果为真,assert什么也不做。

NDEBUG预处理变量

assert的行为依赖于一个名为NDEBUG的预处理变量的状态。如果定义了NDEBUG,则assert什么也不做,默认情况下没有定义NDEBUG。

可以使用#define语句定义NDEBUG,从而关闭调试状态。

6、函数指针

函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和形参类型共同决定,与函数名无关。

int func(int a, string s);

该函数的类型是int(int , string).要想声明一个可以指向该函数的指针,只需要用指针替换函数名即可。

int (*p)(int ,string )  //未初始化

NOTE:*p的括号必须加上

使用函数指针

当把函数名作为一个值使用时,该函数自动地转换成指针。

int (*p)(int ,string )=func;

可以使用函数指针直接调用该函数,而不需要解引用该指针。

指向不同函数类型的指针之间不存在相互转换,可以给函数指针赋值nullptr和0,表示指针没指向任何一个函数。

重载函数的指针

如果定义了指向重载函数的指针,编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数中的某一个精确匹配。

函数指针形参

和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。可以直接把函数作为实参使用,此时它会自动转换成指针。

返回指向函数的指针

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

注意将decltype用于函数名时,返回的是函数类型,而非指针类型,如果要表示函数指针,需要自己加上*。

C++系统学习之六:函数的更多相关文章

  1. 【mongodb系统学习之六】mongodb配置文件方式启动

    六.mongodb可以用配置文件启动,配置文件配好后,每次指定文件就好,而不用每次写一长串: 1).创建配置文件: 2).配置(例如指定数据存储目录,日志存储文件,后台进程,端口号等): 3).配置文 ...

  2. Linux系统学习笔记:文件I/O

    Linux支持C语言中的标准I/O函数,同时它还提供了一套SUS标准的I/O库函数.和标准I/O不同,UNIX的I/O函数是不带缓冲的,即每个读写都调用内核中的一个系统调用.本篇总结UNIX的I/O并 ...

  3. 系统学习机器学习之神经网络(三)--GA神经网络与小波神经网络WNN

    系统学习机器学习之神经网络(三)--GA神经网络与小波神经网络WNN 2017年01月09日 09:45:26 Eason.wxd 阅读数 14135更多 分类专栏: 机器学习   1 遗传算法1.1 ...

  4. 【系统学习ES6】第一节:新的声明方式

    [系统学习ES6] 本专题旨在对ES6的常用技术点进行系统性梳理,帮助大家对其有更好的掌握.计划每周更新1-2篇,希望大家有所收获. 以前用ES5时,声明变量只能用var.ES6的出现,为我们带来了两 ...

  5. 【系统学习ES6】第二节:解构赋值

    [系统学习ES6] 本专题旨在对ES6的常用技术点进行系统性梳理,帮助大家对其有更好的掌握,希望大家有所收获. ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构.解构是一种打 ...

  6. 零基础如何系统学习Java Web

    零基础如何系统学习Java Web?   我来给你说一说 你要下决心,我要转行做开发,这样你才能学成. 你要会打字,我公司原来有一个程序员,打字都是两个手一指禅,身为程序员你一指禅怎么写出的代码,半个 ...

  7. Unity3D 装备系统学习Inventory Pro 2.1.2 基础篇

    前言 前一篇 Unity3D 装备系统学习Inventory Pro 2.1.2 总结 基本泛泛的对于Inventory Pro 这个插件进行了讲解,主要是想提炼下通用装备系统结构和类体系.前两天又读 ...

  8. MES系统学习

    MES系统是当今制造型企业信息化的热点,而统一建模语言UML是面向对象建模的标准语言,在软件工程发挥着重要作用.MES系统如何进行UML建模呢,今天和大家重点讨论一下MES系统的UML建模方法,请看本 ...

  9. JavaScript学习09 函数本质及Function对象深入探索

    JavaScript学习09 函数本质及Function对象深入探索 在JavaScript中,函数function就是对象. JS中没有方法重载 在JavaScript中,没有方法(函数)重载的概念 ...

随机推荐

  1. 学习Mahout (四)

    在Mahout 学习(三)中,我贴了example的代码,里面生成向量文件的代码: InputDriver.runJob(input, directoryContainingConvertedInpu ...

  2. Android NFC P2P

    http://www.nfc.cc/2011/12/28/development-android-beam-and-nfc-peer-2-peer/

  3. [LOJ#10042] 收集雪花

    题目链接: 点我 题目分析: 双指针扫描可以保证在\(O(n)\)的时间复杂度内处理这道题.另外,虽然这个题标签是\(hash\),但是字符串\(hash\)是可以卡掉的,所以建议直接离散化. 维护两 ...

  4. 洛谷 P2623 物品选取

    https://www.luogu.org/problemnew/show/P2623 https://www.luogu.org/blog/test-1/solution-p2623 重点就是甲类物 ...

  5. Jasper_crosstab_measure_display a value of field in crosstab total row

    1.create a measure <measure name="myField" class="java.lang.String"> <m ...

  6. Jasper_crosstab_headerPosition_columngroup header position config - (headerPosition="Stretch")

    i.e <columnGroup name="column11" height="20" totalPosition="Start" ...

  7. python学习之高级特性:

    切片:对列表.元组.字符串.字典取中间的一部分,在C中一般是通过for循环拷贝/memcpy/strcat等操作.而python提供了更方便的切片操作符[m:n]:前闭后开,如果从0取m可以省略:如果 ...

  8. D - 連結 / Connectivity 并查集

    http://abc049.contest.atcoder.jp/tasks/arc065_b 一开始做这题的时候,就直接蒙逼了,n是2e5,如果真的要算出每一个节点u能否到达任意一个节点i,这不是f ...

  9. 通过Chrome执行watir-webdriver

    1.http://code.google.com/p/chromedriver/downloads/list  下载chromedriver驱动文件chromedriver.exe 2.把驱动文件放在 ...

  10. BS3 多级菜单

    <div class="container"> <div class="row"> <h2>Multi level drop ...