以C++ Primer Plus为参考书籍,自身归纳知识点,加深记忆。

第八章 函数探幽

  • 1 内联函数
  • 2 引用变量
    • 2.1 创建引用变量
    • 2.2 引用与函数参数
    • 2.3 引用与结构
    • 何时使用引用参数
  • 默认参数
  • 函数重载
  • 函数模板
    • 显式具体化
    • 实例化与具体化
    • 编译器选择哪个函数版本
      • 完全匹配和最佳匹配
      • 部分排序规则
      • 关键词decltype(C++11)
      • 后置返回类型(C++11)

1 内联函数

常规的函数调用使程序跳到一个地址并在结束时返回,对于内联函数而言,程序无需跳转即可执行,因此内联函数运行速度比常规函数稍快,但代价是占用更多内存。倘若程序在10个不同的地方调用同一个内联函数,该程序则包含该函数代码的10个副本。

要使用内联函数,必须采取下述措施之一:
① 函数声明前加关键字inline
②函数定义前加关键字inline
通常的做法是省略原型,将函数头以及函数代码放在本应提供原型的地方。

#include<iostream>inline double square(double x){return x*x;}int main()
{using namespace std;double a,b;double c=2.0;a = square(5.0);b = square(4.5+1.5);cout<<"a= "<<a<<endl;//25.0cout<<"b= "<<b<<endl;//36.0cout<<"csquare= "<<square(c++)<<endl;//4.0cout<<"c= "<<c<<endl;//3return 0;
}

内联函数与宏
C语言使用预处理器语句#define提供宏
#define SQUARE(X) X*X
本身并不是通过传递参数实现,而是通过文本替换实现
a = SQUARE(5.0); 其实就是a=5.0 * 5.0;
b=SQUARE(4.5+1.5)::::其实就是b=4.5+1.5 * 4.5+1.5
SQUARE(c++):::::(c++) * (c++)不同编译器给出的结果还不一样

内联函数通过传递参数实现,因此在square(c++)中,传递c给square()函数然后再c++,在编译器上没有冲突存在。

2 引用变量

引用变量是C++新增的复合类型,引用是已定义变量的别名,通过将引用变量做参数,函数使用原始数据,而不是副本。

2.1 创建引用变量

int count;
int &cnt = count;//int&指的是指向int的引用,count与cnt指向相同的值和内存单元

上述例子中,倘若对cnt做递增运算,那么count也会做相应的变化。

int count;
int &cnt;
cnt = count;//引用不同于指针,必须在声明时就用初始化该引用。

2.2 引用与函数参数

#include<iostream>
void swapr(int &a,int &b);
void swapp(int *a,int *b);
void swapv(int a,int b);int main()
{using namespace std;int wallet1 = 300;int wallet2 = 350;swapr(wallet1,wallet2);cout<<wallet1<<"  "<<wallet2<<endl;  // 350   300swapp(&wallet1,&wallet2);cout<<wallet1<<"  "<<wallet2<<endl; // 300   350swapv(wallet1,wallet2);cout<<wallet1<<"  "<<wallet2<<endl;//  300  350return 0;
}
void swapr(int &a,int &b)
{int temp;temp = a;a = b;b = temp;
}
void swapp(int *a,int *b)
{int p;p = *a;*a = *b;*b = p;}
void swapv(int a,int b)
{int temp;temp = a;a = b;b = temp;
}

在swapr()中变量a,b是wallet1,wallet2的别名,所以两者交换也将wallet1,2的值进行了交换,但swapv()中的a和b是复制了wallet1,2的新变量,将它俩交换并不影响main()中的变量的值,因此需要利用指针指出wallet1,2内存所在的地址方可进行交换。

2.3 引用与结构

引用非常适合用于结构和类,假设有如下结构定义:

struct free
{string name;int made;int attempts;float percent;
}

则可以这样编写函数原型,在函数中将指向该结构的引用作为参数:

void set_pc(free& ft);
void display(const free& ft);//倘若不想修改传入的结构,关键词const
#include <iostream>
#include <string>
struct free_throws
{std::string name;int made;int attempts;float percent;
};void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws &target, const free_throws &source);int main()
{free_throws one = {"Ifelsa Branch", 13, 14};free_throws two = {"Andor Knott", 10, 16};free_throws three = {"Minnie Max", 7, 9};free_throws four = {"Whily Looper", 5, 9};free_throws five = {"Long Long", 6, 14};free_throws team = {"Throwgoods", 0, 0};free_throws dup;set_pc(one);display(one);accumulate(team, one);display(team);
// use return value as argumentdisplay(accumulate(team, two));accumulate(accumulate(team, three), four);display(team);
// use return value in assignmentdup = accumulate(team,five);std::cout << "Displaying team:\n";display(team);std::cout << "Displaying dup after assignment:\n";display(dup);set_pc(four);
// ill-advised assignmentaccumulate(dup,five) = four;//five的数据给到dup中,再用four覆盖dupstd::cout << "Displaying dup after ill-advised assignment:\n";display(dup);// std::cin.get();return 0;
}void display(const free_throws & ft)
{using std::cout;cout << "Name: " << ft.name << '\n';cout << "  Made: " << ft.made << '\t';cout << "Attempts: " << ft.attempts << '\t';cout << "Percent: " << ft.percent << '\n';
}
void set_pc(free_throws & ft)
{if (ft.attempts != 0)ft.percent = 100.0f *float(ft.made)/float(ft.attempts);elseft.percent = 0;
}free_throws & accumulate(free_throws & target, const free_throws & source)
{target.attempts += source.attempts;target.made += source.made;set_pc(target);return target;
}

何时使用引用参数

对于使用传递的值不做修改的函数:
①数据对象很小,如内置数据类型或小型结构,则按值传递;
②数据对象是数组,则使用指针,并声明为const
③数据对象是大型结构,则使用const指针或者const引用,节省复制结构所需的时间和空间
④数据对象是类对象,使用const引用

对于修改调用函数中数据的函数
①数据对象是内置数据类型,则使用指针
②数据对象是数组,则只能使用指针
③数据对象是结构,则使用引用或指针
④数据对象是类对象,使用引用

默认参数

默认参数指的是当函数调用中省略了实参时自动使用的一个值。
设置默认值必须通过函数原型:

char* left(const char* str, int n =1 );

对于带参数列表的函数,必须从右向左添加默认值

 int harp(int n, int m=2, int j=3);//可用int hard(int n,int m =3, int j);//不可用int saiji(int n =5, int m =1, int j =9);//可用

实参从左到右的顺序依次赋给相应的形参,而不能跳过任何参数。只有在函数原型中指明了默认值,函数定义与没有默认参数时完全相同。

函数重载

默认参数能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)能够使用多个同名的函数,多态是指有多种形式,重载是指有多个重名的函数,两个术语是一回事,但通常使用函数重载。
函数重载的关键是函数的参数列表,也成为函数特征标,如果两个函数的参数数目,排列顺序都相同,则它们的特征标相同,变量名无关紧要。函数重载的条件就是特征标不同。

void print(const char *str,int n);
void print(long l,int n);
void print(double d,int n);
void print(const char *str);

上述函数的特征标均不相同,并且返回类型也可以不同,因此可认为是函数重载

double cube(double x);
double cube(double &x);

编译器在检查函数特征标时,把类型的引用与类型本身视为同一个特征标。

函数模板

函数模板是通用的函数描述,使用泛型来定义函数,其中的泛型可用具体的类型(int,double等)替换,函数模板允许以任意类型的方式定义函数,如以下模板所示:

template<class T>//第一行template必需,class与typename可替换,必须使用尖括号
void swap(T& a,T& b)//类型名可以任意选择,但命名规则要遵守
{T temp;temp = a;a = b;b = temp;
}
// funtemp.cpp -- using a function template
#include <iostream>
// function template prototype
template <typename T>  // or class T
void Swap(T &a, T &b);int main()
{using namespace std;int i = 10;int j = 20;cout << "i, j = " << i << ", " << j << ".\n";cout << "Using compiler-generated int swapper:\n";Swap(i,j);  // generates void Swap(int &, int &)cout << "Now i, j = " << i << ", " << j << ".\n";double x = 24.5;double y = 81.7;cout << "x, y = " << x << ", " << y << ".\n";cout << "Using compiler-generated double swapper:\n";Swap(x,y);  // generates void Swap(double &, double &)cout << "Now x, y = " << x << ", " << y << ".\n";// cin.get();return 0;
}// function template definition
template <typename T>  // or class T
void Swap(T &a, T &b)
{T temp;   // temp a variable of type Ttemp = a;a = b;b = temp;
}

显式具体化

C++98标准:
①对于给定的函数名,可以有非模板函数、模板函数和显式具体化函数及其重载版本
②显示具体化的原型和定义应以template<>打头,并通过原型指出类型
③具体化优先于常规模板,而非模板函数优于具体化和常规模板

//非模板函数
void swap(job&,job&);
//模板函数
template<typename T>
void swap(T& ,T&);
//显式具体化
template<>void swap<job>(job&,job&)//<job>是可选的,job只是一个具体化

实例化与具体化

代码中包含函数模板本身并不会生成函数定义, 只是一个用于生成函数定义的方案,当编译器使用模板为特定类型生成函数定义时,得到的便是函数实例,例如上述swap(i,j)便是模板函数swap()的一个实例,这种实例化的方式成为隐式实例化
显式实例化的语法是声明所需的种类,用<>表示类型,并在声明前加上关键字template:

template void swap<int>(int,int);

显式具体化,显式实例化,隐式实例化统称为具体化,相同之处在于表示的都是使用具体类型的函数定义,而不是通用描述。前缀template与前缀template<>分别代表显示实例化和显式具体化

编译器选择哪个函数版本

完全匹配和最佳匹配

①指向非const数据的指针和引用优先与非const指针和引用参数匹配
②const和非const之间的区别只适用于指针和引用指向的数据
③完全匹配优于另一个的情况还有一个是非模板函数,而另一个不是,此时非模板函数优于模板函数(包括显式具体化)
④如果两者都是模板函数,那么较具体的模板函数优先。

部分排序规则

重载解析寻找最匹配的函数:
如果只存在一个这样的函数,则选择它;
如果存在多个这样的函数,但其中只有一个是非模板函数,则选择非模板函数;
如果存在多个合适的函数,并且都为模板函数,但只有一个比其他更具体,则选择该函数;
如果有多个同样合适的非模板或模板函数,但没有一个更具体的,则会报错;
如果不存在匹配的,也会报错

关键词decltype(C++11)

decltype (x+y)xpy;//xpy的类型为x+y的类型
xpy = x+y;//等价于decltype(x+y)xpy=(x+y)

decltype (expression) var
decltype的确定类型:
①如果expression是一个没有用括号括起的标识符,则var与该标识符的类型相同,包括const等限定符;

double x=2.0;
double y=7.9;
double& rx = x;
const double *ptr;
decltype(x)w;//w是double类型
decltype(rx) u =y;//u是double的引用
decltype(pd)v;//v是一个const double *

②如果expression是一个函数调用,则var与函数返回类型相同;

long indeed(int);
decltype(indeed(3))m;//m是long类型,并且函数没有实际调用

③如果expression是一个左值,则var指向其类型的引用,一种显而易见的情况就是expression是用括号括起的标识符;

double xx = 4.4;
decltype((xx))r2 = xx;//r2是一个double的引用
decltype(xx)W = xx;//w是一个double

④如果前面条件都不满足,那么var与expression类型相同

int j=3;
int &k =j;
int &n =j;
decltype(j+6)x;//x为int
decltype(100L)y;//y为long
decltype(k+n)z;//z为int,k,n为引用,但两者相加得到的是int

后置返回类型(C++11)

对于下面的原型:

double h(int,float);

使用新增的语法可编写成:

auto h(int,float) -> double;//auto是一个占位符,表示后置返回类型提供的类型

2020 我的C++的学习之路 第八章函数相关推荐

  1. 【Python学习笔记】第八章 函数

    [Python学习笔记]第八章 函数 文章目录 [Python学习笔记]第八章 函数 前言 8.1 定义函数 8.2 调用函数 8.2.1 使用位置参数调用函数 8.2.2 使用关键字参数调用函数 8 ...

  2. 2020 我的C++的学习之路 第十章 对象和类

    以C++ Primer Plus为参考书籍,自身归纳知识点,加深记忆. 对象和类 过程性编程与面向对象编程 对象和类 类 类声明 实现类成员函数 方法与对象 类的构造函数与析构函数 声明和定义构造函数 ...

  3. 2020 我的C++的学习之路 第九章 内存模型与名称空间

    以C++ Primer Plus为参考书籍,自身归纳知识点,加深记忆. 内存模型与名称空间 存储持续性 作用域与链接 自动存储持续性 静态持续变量 静态持续性.外部链接性 静态持续性.内部链接性 静态 ...

  4. 2020 我的C++的学习之路

    以C++ Primer Plus为参考书籍,自身归纳知识点,加深记忆. 第四章 复合类型 4.1 数组 数组(array)是一种数据格式,存储多个同类型的值.声明数组应指出数组中元素的类型,元素数量以 ...

  5. 【python3的学习之路七】函数

    返回多个值 import mathdef move(x, y, step, angle=0):nx = x + step * math.cos(angle)ny = y - step * math.s ...

  6. Python学习之路day03——010函数(类似Java中的方法)

    一.函数 函数是一个表示一定功能的代码块,可以在程序中进行应用,书写的方式:"函数名.()". 1.定义一个函数 # -*- coding: gb2312 -*- #随便定义一个h ...

  7. C++PrimerPlus 学习笔记 | 第八章 函数探幽 |3.默认参数 4.函数重载

    默认参数 默认参数是指当函数调用的时候省略了实参自动使用的一个值. // 函数原型 void wow(int n = 1); // 如果这样调用函数等价于 wow(1) wow() // == wow ...

  8. C++PrimerPlus 学习笔记 | 第八章 函数探幽 | 2.引用参数

    引用变量 引用变量的实现 在C++中允许使用 & 符号来声明引用 int a = 1; int & b = a; // b 是 a 的别名且 &b == &a 引用在声 ...

  9. 【无人机】无刷电调学习之路

    ** [无人机]无刷电调的学习之路 ** 本文学习:模吧作者[我爱萝丽爱萝丽]2020新版 无刷电调DIY教程 https://www.moz8.com/forum.php?mod=viewthrea ...

最新文章

  1. linux深度定制,Linux Deepin 12.06 beta1 发布
  2. 2017最新整理传智播客JavaEE第49期 基础就业班
  3. 一天搞定HTML----标签类型与类型转换05
  4. 怎样在命令行中使用FTP
  5. 暗黑破坏神不朽:诅咒宝箱不能开?玩家果断打开后,赚到了
  6. 浅析Hadoop文件格式
  7. Javascript的websocket的使用方法
  8. 李新海:沉默是口才运用的最高境界
  9. 一篇好奇心文,带你看懂基金的运营全貌
  10. ExcelJS 将JSON数组转换成为Excel文件 / JSON转换成为XLSX / 网页导出Excel表格
  11. Ubuntu中双声卡问题
  12. python图片中文字识别
  13. 卸载WPS后office图标异常解决办法
  14. 2022年6月 青少年软件编程(Python) 等级考试试卷(二级)
  15. Docker 配置国内源加速镜像下载
  16. 信息科学技术与创新之“信息纽带”
  17. 高并发下如何快速使用MQ实现缓冲流量,削峰填谷
  18. css hover变成手_html实现鼠标悬停变成手型实现方式
  19. 梦幻答题软件测试,欢乐答题官方测试版
  20. 腾讯安全发布《BOT管理白皮书》|解读BOT攻击,探索防护之道

热门文章

  1. Selenium 与 PhantomJS
  2. Python常用库及模块
  3. 【攻防世界003】re-for-50-plz-50
  4. 生成有控制台的WIN32程序
  5. 开源一个自写的病毒技术工具集
  6. 1.17 Java.util.logging:JDK自带记录日志类
  7. bs4之标签树的上行遍历
  8. Oracle 的基本特点,并完整描述安装过程
  9. JVM中GC小对象配置
  10. JavaScript事件与jQuery方法