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指出函数使用原来的对象但不会修改它。