C++学习笔记之函数拓展:内联函数,宏定义,引用

本文介绍了C++中的内联函数,用于减少函数调用的开销,提高运行速度;宏定义,包括基本用法和常见错误,以及如何防止宏替换错误;引用变量的概念,强调其作为函数参数时的作用,以及与指针的区别。此外,还讨论了引用在结构体和类中的应用。
摘要由CSDN通过智能技术生成

1️⃣内联函数

如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。
为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数

优点:运行速度更快;缺点:消耗的计算机内存空间更多
(如果函数中代码执行时间短,大于调用函数的时间,那么可以采用内联函数)

#include <iostream>
using namespace std;
 
//内联函数,交换两个数的值
inline void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
 
int main()
{
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    swap(&m, &n);
    cout<<m<<", "<<n<<endl;
 
    return 0;
}
 
//运行结果:
//45 99
//45, 99
//99, 45

2️⃣宏定义使用

———————— #define基本用法 ————————

#define命令是C语言中的一个宏定义命令,它用来将一个标识符(宏名)定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。程序编译之前,编译的时候所有的宏名都会被定义的字符串替换,这便是宏替换。
(1)简单的宏定义:
#define <宏名>  <字符串>

例:

#define PI 3.14
float pi2 = PI * 2;//pi2 = 6.28

(2) 带参数的宏定义
#define <宏名> (<参数表>) <宏体>
例:

#define AddOne(x)  (x+1)
float pi2 = PI * 2;//pi2 = 6.28
pi2 = AddOne(pi2);// pi2 = 7.28

———————— 宏替换错误举例 ————————
只要严格遵守“直接替换”,就不会出现下面的问题

比如下面的宏定义:

#define Square(x) x*x

float temp = Square(3+3);
//程序的本意可能是要计算6*6=36,但由于宏定义执行的是直接替换,本身并不做计算,因此实际的结果为 3+3*3+3=15
//想要避免这个问题,只需要修改如下:
#define Square(x) ((x)*(x))

———————— 常用宏定义 ————————
(1) 防止一个头文件被重复包含

#ifndef BODYDEF_H 

#define BODYDEF_H 

 //头文件内容 
#endif

(2) 得到指定地址上的一个字节或字

#define MEM_B( x ) ( *( (byte *) (x) ) ) 

#define MEM_W( x ) ( *( (word *) (x) ) )

//例如:
int bTest = 0x123456;

byte m = MEM_B((&bTest));/*m=0x56*/
int n = MEM_W((&bTest));/*n=0x3456*/

(3) 得到一个field在结构体(struct)中的偏移量

#define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )

(4) 得到一个结构体中field所占用的字节数

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

(5) 得到一个变量的地址(word宽度)

#define B_PTR( var ) ( (byte *) (void *) &(var) ) 

#define W_PTR( var ) ( (word *) (void *) &(var) )

(6) 将一个字母转换为大写

#define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )

(7) 防止溢出的一个方法

#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))

(8) 返回数组元素的个数

#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )

(9) 使用一些宏跟踪调试

ANSI标准说明了五个预定义的宏名。它们是:

LINE:在源代码中插入当前源代码行号;

FILE:在源文件中插入当前源文件名;

DATE:在源文件中插入当前的编译日期

TIME:在源文件中插入当前编译时间;

STDC:当要求程序严格遵循ANSI C标准时该标识被赋值为1;

__cplusplus:当编写C++程序时该标识符被定义

3️⃣引用变量(同一个变量,不同的名称)

那么引用变量是来做什么的呢——引用变量的主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始的数据,而不是其副本(类似指针)。
1.创建引用变量
引用变量是用 & 符号来声明的。如果学过C语言的同学,可能知道 & 可以获取变量的地址,但是C++给 & 符号赋予了另一个函数——声明引用变量。例如,要将 b 作为 a 变量的别名,可以这么做:

int a;
int &b = a; // b 是 a 变量的别名

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

     上述引用声明完后,a和b就指向相同的值和内存单元(即b是a变量的别名)。
#include <iostream>
using namespace std;
int main()
{
	int a = 5;
	int& b = a;
	cout << "a = " << a << ", a address: " << &a << endl;
	cout << "b = " << b << ", a address: " << &b << endl;
	return 0;
}

在这里插入图片描述
语句中的 & 运算符不是地址运算符,而是将 b 的类型声明为 int &,即指向 int 变量的引用:

int a;
int &b;
b = a; // 不能这么做,编译器会报错,a必须先赋值

必须在声明引用时,将其初始化,不能像指针一样,先声明,再赋值:

如果试图改变引用(即从引用初始化的变量,修改为引用别的变量),将会发生情况,如下面这个程序,试图将 a 变量的引用改成 c 变量的引用。

#include <iostream>
using namespace std;
int main()
{
	int a = 5;
	int& b = a;
	cout << "a = " << a << ", a address: " << &a << endl;
	cout << "b = " << b << ", n address: " << &b << endl;
 
	int c = 10;
	b = c; // 试图改变 b 的指向,从指向a 变为 指向c
	cout << "c = " << c << ", c address: " << &c << endl;
	cout << "a = " << a << ", c address: " << &a << endl;
	cout << "b = " << c << ", c address: " << &b << endl;
	return 0;
}

在这里插入图片描述
由于 b 是 a 的别名,因此上述赋值语句相当于下面的这条语句:

a = c; // 重新给a赋值

可能有些人会试图这样做:

声明一个指针 c 来指向 a,然后将 b 引用 *pr,再通过改变 pr 的指向,使其指向 c ,来做到将 b 引用 c。

#include <iostream>
using namespace std;
int main()
{
	int a = 5;
	int* pr = &a;
	int& b = *pr;
	cout << "a = " << a << ", a address: " << &a << endl;
	cout << "*pr = " << *pr << ", pr = " << pr << endl;
	cout << "b = " << b << ", b address: " << &b << endl;
	int c = 10;
	pr = &c;
	cout << "c = " << c << ", c address: " << &c << endl;
	cout << "a = " << a << ", a address: " << &a << endl;
	cout << "*pr = " << *pr << ", pr = " << pr << endl;
	cout << "b = " << b << ", b address: " << &b << endl;
	return 0;
}

在这里插入图片描述
但还是改变不了,b 引用的是 a。如上图,b = 5,并没有变为 c = 10,并且 a 和 b指向的内存单元一致。

2.将引用作为函数参数

#include <iostream>
void swap1(int& a, int& b);
void swap2(int* a, int* b);
void swap3(int a, int b);
int main()
{
	using namespace std;
	int a = 5;
	int b = 10;
	cout << "未交换前: a = " << a << ", b = " << b << endl;
	swap1(a, b);
	cout << "调用引用参数函数后: a = " << a << ", b = " << b << endl;
	swap2(&a, &b);
	cout << "调用指针参数函数后: a = " << a << ", b = " << b << endl;
	swap3(a, b);
	cout << "使用传值函数后: a = " << a << ", b = " << b << endl;
	return 0;
}
 
void swap1(int& a, int& b)
{
	int temp;
 
	temp = a;
	a = b;
	b = temp;
}
 
void swap2(int* a, int* b)
{
	int temp;
 
	temp = *a;
	*a = *b;
	*b = temp;
}
 
void swap3(int a, int b)
{
	int temp;
 
	temp = a;
	a = b;
	b = temp;
}

在这里插入图片描述
在swap1()中,变量 a和b 是函数main()中的 a和b 别名,所以交换 a和b 的值相当于交换main()中 a和b 的值;但在swap3()中,变量 a和b 复制了 main中 a和b 的值的新变量,所以交换 a和b 的值不会影响 main()中 a和b 的值。

3.引用的属性和特别处
例如,需要用两个函数计算参数的平方,其中一个函数接受double类型的参数,另一个接受double引用。

#include <iostream>
using namespace std;
double sq1(double a);
double sq2(double& ra);
int main(void)
{
	double x = 3.0;
	
	cout << "x的平方为:" << sq1(x) << ", 此时x = " << x << endl;
	cout << "x的平方为:" << sq2(x) << ", 此时x = " << x << endl;
	return 0;
}
 
double sq1(double a)
{
	a *= a;
	return a;
}
 
double sq2(double& ra)
{
	ra *= ra;
	return ra;
}

在这里插入图片描述
4.临时变量、引用参数和const
如果实参与引用参数不匹配,C++将生成临时变量。当前,仅当参数为const引用时,C++才允许这样做

double sq2(const double &ra)
{
    return ra*ra;
}

在这里插入图片描述
5.将引用用于结构体
程序例子:

#include <iostream>
#include <string>
 
using namespace std;
 
struct free_throws
{
	string name;
	int made;
	int attempts;
	float percent;
};
 
void dispaly(const free_throws &ft);
void set_pc(free_throws &ft);
free_throws &accumulate(free_throws &target, const free_throws &source);
 
int main()
{
	//6个结构体的数据
	free_throws one = {"xiaoming", 13, 14};
	free_throws two = {"xiaohong", 10, 14};
	free_throws three = {"xiaohuang", 13, 16};
	free_throws four = {"xiaohei", 12, 15};
	free_throws five = {"xiaobai", 14, 16};
	free_throws tim = {"Team", 0, 0};
	
	free_throws dup;
	
	set_pc(one);
	dispaly(one);
	accumulate(tim, one);
	dispaly(tim);
	
	dispaly(accumulate(tim,two));
	accumulate(accumulate(tim,three),four);
	dispaly(tim);
	
	dup = accumulate(tim,five);
	cout << "Displaying tim: " << endl;
	dispaly(tim);
	cout << "Displaying dup after assignment: " << endl;
	dispaly(dup);
	set_pc(four);
	
	accumulate(dup,five) = four;
	cout << "Displaying dup after ill-advised assignment: " << endl;
	dispaly( dup);
	
	return 0;
}
 
void dispaly(const free_throws &ft)
{
	cout << "Name: " << ft.name << '\n';
	cout << "Made: " << ft.made << '\t';
	cout << "Attempts: " << ft.attempts << '\t';
	cout << "Percent: " << ft.percent << endl;
}
 
void set_pc(free_throws &ft)
{
	if (ft.attempts != 0)
		ft.percent = float(ft.made) / float(ft.attempts) * 100.0;
	else
		ft.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;
}

运行结果:

Name: xiaoming
Made: 13        Attempts: 14    Percent: 92.8571
Name: Team
Made: 13        Attempts: 14    Percent: 92.8571
Name: Team
Made: 23        Attempts: 28    Percent: 82.1429
Name: Team
Made: 48        Attempts: 59    Percent: 81.3559
Displaying tim:
Name: Team
Made: 62        Attempts: 75    Percent: 82.6667
Displaying dup after assignment:
Name: Team
Made: 62        Attempts: 75    Percent: 82.6667
Displaying dup after ill-advised assignment:
Name: xiaohei
Made: 12        Attempts: 15    Percent: 80
 
--------------------------------
Process exited after 0.1005 seconds with return value 0
请按任意键继续. . .

6.将引用用于类

#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1,const string & s2);
const string & version2(string & s1,const string & s2);
const string & version3(string & s1,const string & s2);
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;

	result = version3(input, "@@@");
	cout << "Your string enhanced: " << result << endl;
	cout << "Your original string: " << input << endl;

	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)
{
	s1 = s2 + s1 + s2;
	return s1;
}

const string & version3(string & s1, const string & s2)
{
	string temp;
	temp = s2 + s1 + s2;
	return temp;
}


Enter a string :AAAAAA
Your string as entered:AAAAAA
Your string enhanced: ***AAAAAA***
Your original string: AAAAAA
Your string enhanced: ###AAAAAA###
Your original string: ###AAAAAA###

该程序接受两个string字符串,并使用string类的相加功能来创建一个满足要求的新字符串,这两个参数都是const引用,在这种情况下,使用引用的效率更高,因为函数不需要创建新的string对象,并将原来的数据复制到新对象中,const指出函数使用原来的对象但不会修改它。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值