一、C++内联函数

内联函数是C++为提高程序运行速度所做的改进。常规函数和内联函数之间的主要区别不在于编写方式,而在于C++编译器如何将他们组合到程序中。
编译过程的最终产品是可执行程序。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。计算机随后将逐步执行这些指令。有时候循环或者分支语句,将跳过一些指令,向前或向后跳到特定的地址。常规函数调用也使程序跳到另一个函数的地址,并在函数结束时返回。执行函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈,保留内存块,跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入寄存器中),然后跳回到地址被保存的指令处。
C++内联函数提供了另一种选择。内联函数的编译代码与其他程序代码的“内联”起来,也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。
应该有选择地使用内联函数。如果执行函数代码地时间比处理函数调用机制地时间长,则节省的时间将只占用整个过程的很小一部分。如果代码执行时间很短,则内联调用使用大部分时间。另一方面,由于这个过程相当快,因此节省了该过程的大部分时间,但节省的时间绝对值并不大,除非函数值经常被调用。
要使用这项特性,必须采取下述措施之一:
1.在函数声明前加上关键字inline;
2.在函数定义前加上关键字inline。
通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方。

#include <iostream>
// an inline function definition
inline double square(double x) { return x * x; }
int main()
{using namespace std;double a, b;double c = 13.0;a = square(5.0);b = square(4.5 + 7.5);   // can pass expressionscout << "a = " << a << ", b = " << b << "\n";cout << "c = " << c;cout << ", c squared = " << square(c++) << "\n";cout << "Now c = " << c << "\n";// cin.get();return 0;
}

内联函数和常规函数一样,也是按值来传递的,如果参数为表达式即4.5+7.5,则函数将传递表达式的值12。这使得C++内联功能远远胜过C语言的宏定义。
尽管程序没有提供独立的原型,但C++原型特性仍在起作用。这是因为在函数首次使用前出现整个函数定义充当了原型,这意味着可以给square()传递int和long值,将值传递给函数前,程序自动将这个值强制转换为double类型。

二、引用变量

C++新增了一种复合类型——引用变量。引用是已定义的变量的别名,如果将twain作为clement变量的引用,则可以交替使用twain和clement来表示该变量。引用变量的主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本,这样除指针之外,引用也为函数处理大型结构提供了一种非常方便的途径,同时对于设计类来说,引用是必不可少的。
1.创建引用变量
C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义,将其用来声明引用。

int rats;
int & rodents = rats; //makes rodents an alias for rats

其中,&不是地址运算符,而是类型标识符的一部分。就像声明中的char*指的是指向char的指针一样,int&指的是指向int的引用。

#include <iostream>
int main()
{using namespace std;int rats = 101;int & rodents = rats;   // rodents is a referencecout << "rats = " << rats;cout << ", rodents = " << rodents << endl;rodents++;cout << "rats = " << rats;cout << ", rodents = " << rodents << endl;
// some implementations require type casting the following
// addresses to type unsignedcout << "rats address = " << &rats;cout << ", rodents address = " << &rodents << endl;// cin.get();return 0;
}


int & rodents = rats;中的&运算符不是地址运算符,而是将rodents的类型声明为int&,即指向int变量的引用,
但是, cout << ", rodents address = " << &rodents << endl;中的&的运算符是地址运算符,其中&rodents表示rodents引用的变量的地址。
在上述的例子中,rats和rodents的值和地址都相同,将rodents加1将影响这两个变量的值,更准确的讲rodents++操作讲一个有两个名称的变量加1。

 int rats = 101;int & rodents = rats; // rodents is a referenceint * prats = &rats; // parts a pointer

表达式rodents和*prats都可以同rats互换,而表达式&rodents和prats都可以同&rats互换。从这一点来讲,引用看上去很像伪装表达式的指针,但是引用又不同于指针,除了表示法不同以外,差别之一就是必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值。

int rat;
int & rodent;
rodent = rat; //No,you can't do this.
//必须在声明引用变量时进行初始化。

引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它,也就是说:
int & rodents = rats ;
实际上是下述代码的伪装表示:
int * const pr =&rats;

#include <iostream>
int main()
{using namespace std;int rats = 101;int & rodents = rats;   // rodents is a referencecout << "rats = " << rats;cout << ", rodents = " << rodents << endl;cout << "rats address = " << &rats;cout << ", rodents address = " << &rodents << endl;int bunnies = 50;rodents = bunnies;       // can we change the reference?cout << "bunnies = " << bunnies;cout << ", rats = " << rats;cout << ", rodents = " << rodents << endl;cout << "bunnies address = " << &bunnies;cout << ", rodents address = " << &rodents << endl;// cin.get();return 0;
}

最初,rodents引用的是rats,随后程序试图将rodents作为bunnies的引用:rodents=bunnies;
这种意图是成功的,因为rodents的值从101变为了50。但是同时rats也变成了50,同时rats和rodents的地址也相同,而该地址与bunnies的地址不同。由于rodents是rats的别名,因此上述赋值语句与下面的语句等效:rats = bunnies;
也就是说,这意味着将bunnies变量的值赋给rat变量,简而言之,可以通过初始化声明来设置引用,但是不能通过赋值来设置。
2.将引用用作函数参数
引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量。
交换两个变量的值:交换函数必须能够修改调用程序中变量的值。这意味着按值传递变量将不管用,因为函数将交换原始变量副本的内容,而不是变量本身的内容。但是传递引用时,函数可以使用原始数据,另一种方法是,传递指针来访问原始数据。

#include <iostream>
void swapr(int & a, int & b);   // a, b are aliases for ints
void swapp(int * p, int * q);   // p, q are addresses of ints
void swapv(int a, int b);       // a, b are new variables
int main()
{using namespace std;int wallet1 = 300;int wallet2 = 350;cout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Using references to swap contents:\n";swapr(wallet1, wallet2);   // pass variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Using pointers to swap contents again:\n";swapp(&wallet1, &wallet2); // pass addresses of variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Trying to use passing by value:\n";swapv(wallet1, wallet2);   // pass values of variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;// cin.get();return 0;
}
void swapr(int & a, int & b)    // use references
{int temp;temp = a;       // use a, b for values of variablesa = b;b = temp;
}
void swapp(int * p, int * q)    // use pointers
{int temp;temp = *p;      // use *p, *q for values of variables*p = *q;*q = temp;
}
void swapv(int a, int b)        // try using values
{int temp;temp = a;      // use a, b for values of variablesa = b;b = temp;
}


void swapr(int & a, int & b); //pass variables
void swapp(int * p, int * q); //pass addresses of variables
void swapv(int a, int b); //pass values of variables
按引用传递swapr和按值传递swapv看起来相同。只能通过原型或函数定义才能知道swapr是按引用传递。然而地址运算符&使得按地址传递swapp。
3.引用的属性个特别之处

#include <iostream>
double cube(double a);
double refcube(double &ra);
int main ()
{using namespace std;double x = 3.0;cout << cube(x);cout << " = cube of " << x << endl;cout << refcube(x);cout << " = cube of " << x << endl;// cin.get();return 0;
}
double cube(double a)
{a *= a * a;return a;
}
double refcube(double &ra)
{ra *= ra * ra;return ra;
}


refcube()函数修改了main()中x的值,而cube()没有,这提醒我们为何通常按值传递,变量a位于cube()中,它被初始化x的值,但修改a并不会影响x。但由于refcube()使用了引用参数,因此修改ra实际上就是修改x。
创建临时变量:1.实参的类型是正确,但不是左值;2.实参的类型不正确,但可以转换为正确的类型。
4.将引用用于结构
引用引入主要是为了用于结构和类,而不是基本的内置类型。
使用结构引用参数的方式与使用基本变量引用相同,只需在声明结构参数时使用引用运算符&即可。

#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;std::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;
}


5.将引用用于类对象
将类对象传递给函数时,C++通常的做法是使用引用。可以通过使用引用,让函数将类string、ostream、istream、ofstream和i发stream等类的对象作为参数。

#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2);  // has side effect
const string & version3(string & s1, const string & s2);  // bad design
int main()
{string input;string copy;string result;cout << "Enter a string: ";getline(cin, input);copy = input;cout << "Your string as entered: " << input << endl;result = version1(input, "***");cout << "Your string enhanced: " << result << endl;cout << "Your original string: " << input << endl;result = version2(input, "###");cout << "Your string enhanced: " << result << endl;cout << "Your original string: " << input << endl;cout << "Resetting original string.\n";input = copy;result = version3(input, "@@@");cout << "Your string enhanced: " << result << endl;cout << "Your original string: " << input << endl;// cin.get();// cin.get();return 0;
}
string version1(const string & s1, const string & s2)
{string temp;temp = s2 + s1 + s2;return temp;
}
const string & version2(string & s1, const string & s2)   // has side effect
{s1 = s2 + s1 + s2;
// safe to return reference passed to functionreturn s1;
}
const string & version3(string & s1, const string & s2)   // bad design
{string temp;temp = s2 + s1 + s2;
// unsafe to return reference to local variablereturn temp;
}


6.何时使用引用参数
主要原因:1.能够修改调用函数中的数据类型;2.通过传递引用而不是整个数据对象,可以提高程序的运行速度。

三、默认参数

默认参数指的是当函数调用中省略了实参时自动使用的一个值。如果将void wow(int n)设置成n个有默认值为1,则函数调用wow()相当于wow(1)。这极大地提高了使用函数的灵活性。

#include <iostream>
const int ArSize = 80;
char * left(const char * str, int n = 1);
int main()
{using namespace std;char sample[ArSize];cout << "Enter a string:\n";cin.get(sample,ArSize);char *ps = left(sample, 4);cout << ps << endl;delete [] ps;       // free old stringps = left(sample);cout << ps << endl;delete [] ps;       // free new string// cin.get();// cin.get();return 0;
}
// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{if(n < 0)n = 0;char * p = new char[n+1];int i;for (i = 0; i < n && str[i]; i++)p[i] = str[i];  // copy characterswhile (i <= n)p[i++] = '\0';  // set rest of string to '\0'return p;
}


该程序使用new创建一个新的字符串,以存储被选择的字符。

四、函数重载

默认参数是能够让使用不同数目的参数调用一个函数,而函数重载能够使用多个同名的函数。“函数重载”指的是可以有多个同名的函数,因此对名称进行重载。
函数重载的关键是函数的参数列表——也称为函数特征标。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的。

#include <iostream>
unsigned long left(unsigned long num, unsigned ct);
char * left(const char * str, int n = 1);
int main()
{using namespace std;char * trip =(char *) "Hawaii!!";   // test valueunsigned long n = 12345678; // test valueint i;char * temp;for (i = 1; i < 10; i++){cout << left(n, i) << endl;temp = left(trip,i);cout << temp << endl;delete [] temp; // point to temporary storage}// cin.get();return 0;
}
// This function returns the first ct digits of the number num.
unsigned long left(unsigned long num, unsigned ct)
{unsigned digits = 1;unsigned long n = num;if (ct == 0 || num == 0)return 0;       // return 0 if no digitswhile (n /= 10)digits++;if (digits > ct){ct = digits - ct;while (ct--)num /= 10;return num;         // return left ct digits}else                // if ct >= number of digitsreturn num;     // return the whole number
}
// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{if(n < 0)n = 0;char * p = new char[n+1];int i;for (i = 0; i < n && str[i]; i++)p[i] = str[i];  // copy characterswhile (i <= n)p[i++] = '\0';  // set rest of string to '\0'return p;
}


只有当函数基本上执行相同的任务,但使用不同形式的数据时,才采用函数重载。

五、函数模块

函数模块是通用的函数描述,也就是使用泛型来定义函数,其中泛型可用具体的类型(int或者double)替换。通过将类型作为参数传递给模板,也可以使用编译器生成该类型的函数。
由于模板允许以泛型(而不是具体类型)的方式来编写程序,因此有时候也被称为通用编程,由于类型是用参数表示的,因此模板特性有时也被称为参数化类型。
建立一个交换模板:

template<typename AnyType>
void Swap(AnyType &a, AnyType &b)
{AnyType temp;temp = a;a = b;b = temp;
}

第一行指出,要建立一个模板,并将类型命名为AnyType,关键字template和typename是必需,除非可以使用关键字class和typename。另外,必须使用尖括号。类型名可以任意选择,只要遵循C++的命名规则即可。模板不创建任何函数,而只是告诉编译器如何定义函数。
将模板放在头文件中,并在需要使用模板的文件中包含头文件。
函数模板自动完成重载函数的过程。只需使用泛型和具体算法来定义函数,编译器将为程序中使用的特定参数类型生成正确的函数定义。

读书笔记||函数探幽相关推荐

  1. 《流畅的Python第二版》读书笔记——函数作为一等对象

    引言 这是<流畅的Python第二版>抢先版的读书笔记.Python版本暂时用的是python3.10.为了使开发更简单.快捷,本文使用了JupyterLab. 函数是Python的一等( ...

  2. 《流畅的Python第二版》读书笔记——函数中的类型注解

    引言 这是<流畅的Python第二版>抢先版的读书笔记.Python版本暂时用的是python3.10.为了使开发更简单.快捷,本文使用了JupyterLab. 本章关注于Python在函 ...

  3. 《javascript语言精粹》读书笔记——函数

    这几天发现了一本好书,又薄又精辟,<JavaScript语言精粹> 看了对象.函数这两节,发现书如其名,确实是精粹. 函数的调用 函数调用的方式有四种: - 方法调用模式 - 函数调用模式 ...

  4. vb 数组属性_[读书笔记]CSAPP:7[VB]机器级表示:函数

    视频地址: [精校中英字幕]2015 CMU 15-213 CSAPP 深入理解计算机系统 课程视频_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com 课件地址: ...

  5. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(三)】函数调用与栈(this指针、返回值传递临时对象构建栈、运行库与多线程、_main函数、系统调用与中断向量表、Win32、可变参数、大小端

    文章目录 前言 介绍 内存 内存布局 栈与调用惯例 堆与内存管理 运行库 入口函数和程序初始化 C/C++运行库 运行库与多线程 C++全局构造与析构 fread 实现 系统调用与API 系统调用介绍 ...

  6. 读书笔记:《流畅的Python》第五章 一等函数

    # 一等对象/一等函数 ''' 1.在运行时创建 2.能赋值给变量或数据结构中的元素 3.能作为函数的参数传给函数 4.能作为函数的返回值返回结果 '''# 函数对象本身时function对象的实例d ...

  7. mysql函桌为之一的_MYSQL必知必会读书笔记第十和十一章之使用函数处

    mysql简介 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. 拼接字段 存储在数据库表中的 ...

  8. Kotlin读书笔记之内联函数、扩展函数、匿名函数、lambda

    本文主要涉及内联函数.扩展函数.lambada以及匿名函数等.作为读书笔记对于细节深入没有过多的扩展,后续将对于各个知识点作进一步的研度.本文的内容主要是参考官方教程以及博客内容,作为读书笔记以及后续 ...

  9. C++ primer plus读书笔记与心得

    C++Primer plus读书笔记与心得 2020年过年期间,因新型冠状肺炎影响,推迟复工.根据公司读书计划,将c++ primer plus 这本书复习了一遍,并将其中一些章节中自己记忆模糊或者之 ...

最新文章

  1. andriod studio 注释乱码问题
  2. springboot配置文件priperties大全
  3. vim——打开多个文件、同时显示多个文件、在文件之间切换
  4. 网络嗅探混杂模式与非混杂模式的区别
  5. BZOJ 3990: [SDOI2015]排序(搜索+剪枝)
  6. 程序实践:命令行之连连看
  7. [HNOI2009]有趣的数列
  8. java dfs算法蓝桥杯题_【蓝桥杯省赛JavaB组真题详解】四平方和(2016)_疼疼蛇的博客-CSDN博客...
  9. LookUpEditPopup自动调整宽度
  10. 卧槽!二维码要被扫完了吗?疫情期间竟用掉了1400亿个!
  11. XML和实体序列化和反序列化
  12. http抓包实践--(三)--HTTP协议中的缓存
  13. spring boot酒店会员点餐系统毕业设计源码072005
  14. sql实现查询学习了所有课程的学生信息
  15. python 换脸 github_如何用200行Python代码“换脸”
  16. ThreadX_笔记
  17. 超级无敌神奇的java_超级无敌神奇的java期末考试题库.doc
  18. EMC VNX5200 故障灯亮,但无任何硬件故障提示
  19. 李宏毅机器学习--self-supervised:BERT、GPT、Auto-encoder
  20. xdlove的暑期实践总结(大二篇)

热门文章

  1. Linux的mask什么意思中文,mask是什么意思
  2. IBM WCM辛迪加数据同步
  3. HTML5、CSS3应用教程之 跟DIV说Bey!Bey!
  4. h5微信f分享链接给对方获取对方手机号_微信生日贺卡链接制作
  5. 万代南梦宫假面骑士时尚品牌HENSHIN by KAMEN RIDER首登中国;安宏资本宣布与资生堂达成协议 | 知消...
  6. 达人评测 i3 12100和i5 12400选哪个
  7. 梯形断面临界水深莫洛图
  8. mysql5.7导出数据提示--secure-file-priv选项问题的解决方法
  9. 中标麒麟+QT+达梦数据库
  10. 在windows上搭建React Native开发环境