当前位置:网站首页>第8章 函数探幽 -1
第8章 函数探幽 -1
2022-08-11 05:15:00 【呀津克丝索】
待定
本章知识点:
内联函数
引用变量
按引用传递函数参数
默认参数
函数重载
函数模板
函数模板具体化
8.1 C++内联函数
内联函数是为提高程序运行速度。与常规函数之间的主要区别不在于编写方式,而是C++编译器如何将它们组合到程序中。
常规函数调用使程序跳到另一个地址(函数的地址),并在函数结束时返回。来回跳跃并记录跳跃位置需要一定的开销。内联函数的编译代码与其他程序代码“内联”,即编译器使用相应的函数代码替换函数调用,对于内联代码,程序虚无需跳到另一个位置处执行代码,再跳回来。因此,内联函数运行速度比常规函数快,但代价是需占用更多内存。所以,应当有选择地使用内联函数(执行代码时间和调用函数花费时间)。
使用内联函数:
在函数声明前加上关键字inline;
在函数定义前加上关键字inline。
通常做法:省略原型(声明过程),只写函数定义和调用,并将其放在本应提供原型的地方。内联函数仅适用于代码量很少的情况下。
注意:函数过大或函数调用自己(内联函数不能递归)时,编译器不会将其作为内联函数。
例子:
# include<iostream>
using namespace std;
inline double square(double x) {
return x*x;} //编译器把此条语句既当作函数定义亦当作//函数原型
int main(void)
{
double a, b;
double c = 13.0;
a = square(5.0);
b = square(4.5 + 7.5);
cout << “a = ” << a << “, b = ” << b << endl;
cout << “c = ” << c << endl;
cout << “now c square = ” << square(c++) << endl;
}
内联与宏
计算平方的宏:
#define SQUARE(X) X*X
上式并非通过传递参数实现,而是通过文本替换来实现:
a = SQUARE(5.0); //is replaced by a = 5.0 * 5.0 valid
b = SQUARE(4.5 + 7.5); // b = 4.5 + 7.5 * 4.5 + 7.5
c = SQUARE(c++); // d = c++ * c++
上述3式只有a可以正常运行,b可通过添加()
改进:
#define SQUARE(X) ( (X) * (X))
而对于c式中的“c++”仍将递增两次(非按值传递)。
总结:对于简短函数代码尽量避免使用函数宏,相应选择内联函数实现。
8.2 引用变量
引用常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名(形参和实参是一回事),这种传递方法称为按引用传递。按值传递,形参操作的是实参的拷贝,而按引用传递则是实参本身。
C语言只能按值传递,但也可以用指针传递避开按值传递的限制。
经典例子:
# include<iostream>
using namespace std;
void Swapr(int &a, int &b);
void Swapp(int *pa, int *pb);
Void Swapv(int a, int b);
int main(void)
{
Int wallet1 = 300;
Int wallet2 = 350;
Cout << “wallet1 : ”<< wallet1 << endl; //300
Cout << “wallet2 : ”<<wallet2 << endl; //350
Cout << “Using & to swap contents” << endl;
Swapr(wallet1, wallet2);
Cout << “wallet1 : ”<< wallet1 << endl; //350
Cout << “wallet2 : ”<<wallet2 << endl; //300
Cout << “Using * to swap contents” << endl;
Swapp(&wallet1, &wallet2);
Cout << “wallet1 : ”<< wallet1 << endl; //300
Cout << “wallet2 : ”<<wallet2 << endl; //350
Cout << “Using value to swap contents” << endl;
Swapv(wallet1, wallet2);
Cout << “wallet1 : ”<< wallet1 << endl; //300
Cout << “wallet2 : ”<<wallet2 << endl; //350
Return 0;
}
对应函数定义:
//引用
Void Swapr(int &a, int &b)
{
Int temp;
Temp = a;
A = b;
B = temp;
}
//指针
Void Swapp(int *pa, int *pb)
{
Int temp;
Temp = *pa;
*pA = *pb;
*B = temp;
}
//按值
Void Swapv(int a, int b)
{
Int temp;
Temp = a;
A = b;
B = temp;
}
上述按值传递无法实现两个数的交换。
比较两个函数:
按值:
double cube(double a)
{
a *= a * a;
return a;
}
int x = 3;
cout << cube(x) << “ = cube of ” << x << endl;
27 = cube of 3
//操作的是x的副本,未被修改
引用:
double recube(double &a)
{
a *= a * a;
return a;
}
int x = 3;
cout << recube(x) << “= cube of ” <<x<< endl;
27 = cube of 27
//操作的是x本身,已被修改
当数据量比较大(结构体或类)时,函数的形参推荐使用引用。
上式右边函数可修改为:
double recube(double &a)
{
return a * a * a;
}
int x = 3;
/***********************************/
| recube(5) << “= cube of ” << “5” | recube(5) <<“= cube of ” <<”5” |
| 125 = cube of 5 | Invalid,程序出错 |
//recube(5)之所以不合法,因为ra必须为变量的引用,而非常量的引用。
| recube(5.0+x) << “= cube of ” <<”5.0+x” | recube(5+x) <<“= cube of ” <<” 5+x” |
| 512 = cube of 5 + x | Invalid, 程序出错 |
//recube(5+x)之所以不合法,因为ra必须为变量的引用,而非表达式的引用。
double recube(const double &a)
{
return a * a * a;
}
Int x =3;
cout << recube(5) << “= cube of ” << “5”<< endl;
//输出
125 = cube of 5
仅当参数为关键字const引用时,若实参与引用参数不匹配,C++将生成临时变量,将函数调用的参数的值传递给该临时变量,并让参数来引用该变量。上述代码将5传递给一个临时变量,再通过临时变量与ra关联起来(同适用于表达式)。临时变量使得函数在可处理的参数种类方面更通用(常量,表达式)。
应尽可能使用const
将引用参数声明为常量数据的引用的理由:
使用const可以避免无意中修改数据的编程错误;
使用const使函数可处理const和非const实参,否则将只能接受非const数据;
使用const引用使函数能够正确生成并使用临时变量。
应尽可能将引用形参声明为const。
8.2.4 引用用于结构
引用非常适合用于结构和类,且引用的引入也主要是为了这些类型,而非基本内置类型。使用结构引用参数的方式与使用基本变量引用相同,只需在声明结构参数时使用引用运算符&
即可。
例如:
struct free_throws
{
std::string name;
int made;
int attempts;
float percent;
}
//在函数中将指向该结构的引用作为参数,对应的函数原型,:
void set_pc(free_throws & ft);
void display(const free_throws & ft); //不希望传入的结构被修改
1.程序说明:
2.为何要返回引用
dup = accumulate(team, five);
如果accumulate()
返回一个结构而非引用,程序将把整个结构复制到一个临时位置(临时开辟的内存空间),再将其拷贝赋值给dup;但当返回值会引用时,将直接把team
赋值到dup
,效率更高。
3. 返回引用时需注意的问题
返回引用最重要的一点:应避免返回函数终止时不再存在的内存单元引用。即如下代码:
const free_throws & clone2(free_throws & ft)
{
free_throws newguy;
newguy = ft;
return newguy;
}
上述函数返回一个指向临时变量(newguy)的引用,函数运行时它将不再存在。同时也应避免返回指向临时变量的指针。
为避免这种问题:
最简单的方式是返回一个作为参数传递给函数的引用(上述代码accumulate()
便是如此);
另一种方法使用new
来分配新的存储空间,。
下面是使用引用来完成类似工作:
Const free_throws & clone(free_throws & ft)
{
free_throws *pt; //创建一个无名的free_throws结构,并让指针pt指向该结构
*pt = ft; //
return *pt;
}
上述结束后指针不存在,但指针指向的内容还存在。
8.2.5 将引用用于类对象
将类对象传递给函数时,C++通常使用引用。
程序例子:
函数version1()
,temp
是一个新的string对象,只在version1()
中有效,该函数执行完毕后将不再存在,因此无法返回指向temp的引用
,所以该函数的返回类型为string。也就是temp的内容将被复制到一个临时开辟的内存空间中,在main()
中被调用时再将其复制到result
中。
函数version2()
不创建临时string对象,而是直接修改原来的string对象——形参s1
(无const修饰)。s1
是main()
中input的引用
,而非函数中的引用。
函数version3()
,致命缺陷:返回一个指向version3()
中声明的变量的引用。该函数执行完毕后,temp
将不再存在(并非谁谁的引用),无法引用。
result = version3(input, “@@@”); //程序试图引用已经释放的内存
8.2.6 对象、继承和引用
ofstream对象可以使用ostream类的方法,使得文件输入/输出的格式与控制台输入/输出相同。因为ostream是基类(ofstream建立在ostream基础之上),ofstream是派生类(从ostream派生而来)
使得能够将特性从一个类传递给另一个类的语言特性被称为继承。
派生类继承了基类的方法,可以使用基类的特性;
基类的引用可以指向派生类对象而无需进行强制类型转换,即定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数。
8.2.7 何时使用引用参数
使用引用参数的主要原因:
程序员可修改调用函数中的数据对象
通过传递引用而非整个数据对象可以提高程序的运行速度
以上也是使用指针参数的原因。当数据对象较大(如结构和类对象)第二个原因最重要。
- 对于使用传递的值而不作修改的函数:
数据对象很小(内置数据类型或小型结构),按值传递
数据对象为数组,使用指针,这也是唯一的选择,并将指针声明为指向const的指针
数据对象是较大的结构,使用const指针或引用,提高程序效率(节省时间和空间)
数据对象是类对象,使用const引用。类设计通常要求使用引用。 - 对于修改调用函数中数据的函数:
数据对象是内置数据类型,则使用指针或引用(比如看到fixit(&x),其中x是int,则表示该函数将修改x)
数据对象是数组,只能使用指针
数据对象是结构体,使用引用或指针
数据对象是类,使用引用。
以上为指导原则,也可根据实际情况做其他操作。
边栏推荐
猜你喜欢
随机推荐
You must understand - the nine built-in objects and four domain objects of JSP
【Redis】Redis 的安装及图形化界面 Redis DeskTop Manager 的安装与使用
并发编程之线程基础
【背包】采药题解
基础数据之double和float区别
task02 fashion-mnist分类实战
Configure checkstyle in IDEA
普林斯顿概率论读本读书笔记(阅读中......)
(2) Docker installs Redis in practice (persistent AOF and RDB snapshots)
第9章 内存模型和名称空间
阿里天池学习赛 新闻文本分类
Pytorch最全安装教程(一步到位)
(1) Docker installs Redis in practice (one master, two slaves, three sentinels)
自制病毒——整蛊
面试宝典一: code题目记录
基于TF-IDF 文本相似性实战 详细教程
(二)性能实时监控平台搭建(Grafana+Prometheus+Jmeter)
吃瓜教程task01 第2章 模型评估与选择
课堂练习--0708
判断一个字符串是否为空,如果为空,对其赋值,如果不为空,获取字符的个数并打印第一个字符