CPP-08-函数探幽

内联函数

编译过程的最终产品是可执行程序——由一组机器语言指令组成。 运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。计算机随后将逐步执行这些指令。有时(如有循环或分支语句时),将跳过一些指令,向前或向后跳到特定地址。常规函数调用也使程序跳到另一个地址(函数的地址),并在函数结束时返回

执行到函数调用指令时, 程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到 堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入到寄存器中),然后跳回到地址被保存的指令处。来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。

对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。所以内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存

cpp_inline_0729_1926

要使用内联函数,需要采取以下措施

  • 在函数声明前加上关键字 inline
  • 在函数定义前加上关键字 inline
1
2
3
4
5
6
inline double square(int x) 
{
return x * x;
}

cout << square(2) << endl; //4

引用变量

引用是已定义的变量的别名(另一个名称)

1
2
3
4
5
6
7
8
9
10
11
int rats = 100;
int nums = 10;
int & rats_back = rats;
//int & rats_back; //声明引用变量时进行初始化
cout << rats << " " << rats_back << endl;
rats = 101;
cout << rats << " " << rats_back << endl;
rats_back = nums;
cout << rats << " " << rats_back << " " << nums << endl;
nums = 1;
cout << rats << " " << rats_back << " " << nums << endl;

运行结果

1
2
3
4
100 100
101 101
10 10 10
10 10 1

使用 int & rats_back = rats; 声明引用变量,即指向 int 变量的引用

  1. 必须在声明引用变量时进行初始化
  2. 一旦与某个变量关联起来,就将一直效忠于它

引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量。C++新增的这项特性是对C语言的超越,C语言只能按值传递

cpp_bieming_0729_1950
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void swapr(int & a, int & b)   //a, b are aliases for ints
{
int temp;
temp = a;
a = b;
b = temp;
}

void swapp(int * p, int * q) //p, q are addreesses of ints
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}

void swapv(int a, int b) //a, b are new variables
{
int temp;
temp = a;
a = b;
b = temp;
}

int wallet1 = 300;
int wallet2 = 350;
swapr(wallet1, wallet2); //引用传递
cout << wallet1 << " " << wallet2 << endl;
swapp(&wallet1, &wallet2);
cout << wallet1 << " " << wallet2 << endl;
swapv(wallet1, wallet2);
cout << wallet1 << " " << wallet2 << endl;

运行结果

1
2
3
350 300
300 350
300 350

swapp在函数使用p和q的整个过程中使用解除 引用运算符*,所以跟 swapv 效果一样

这就是需要注意的地方,通过引用传递可能修改源值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
double cube(double a)
{
a = a * a;
return a;
}

double refcube(double &ra)
{
ra = ra * ra;
return ra;
}

double a = 2;
re
cout << cube(a) << " " << a << endl; //4 2
cout << refcube(a) << " " << a << endl; //4 4
refcube(a + 0.5); //candidate function not viable: expects an lvalue for 1st argument

可以通过 const 不允许函数修改

1
double refcube(const double &ra);

所以,将引用参数声明为常量数据的引用的理由有三个:

  • 使用const可以避免无意中修改数据的编程错误;
  • 使用const使函数能够处理const和非const实参,否则将只能接受非const数据;
  • 使用const引用使函数能够正确生成并使用临时变量。

结构体的引用变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct student
{
string name;
int age;
double weight;
};

void Add(student & t)
{
t.name = "hello";
}

student Change(student t)
{
t.name = "nihao";
return t;
}

student t = student{
"world",
18,
77.5
};
Add(t);
cout << t.name << " " << t.age << " " << t.weight << endl;

student t1 = Change(t);
cout << t.name << " " << t.age << " " << t.weight << endl;
cout << t1.name << " " << t1.age << " " << t1.weight << endl;

运行结果

1
2
3
hello 18 77.5
hello 18 77.5
nihao 18 77.5

什么时候使用引用

  • 如果数据对象很小,如内置数据类型或小型结构,则按值传递。
  • 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针。
  • 如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的效率。这样可以节省复制结构所需的时间和空间。
  • 如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象 参数的标准方式是按引用传递。

对于修改调用函数中数据的函数:

  • 如果数据对象是内置数据类型,则使用指针
  • 如果看到诸如 fixit(&x),则该函数将修改 x。
  • 如果数据对象是数组,则只能使用指针。
  • 如果数据对象是结构,则使用引用或指针
  • 如果数据对象是类对象,则使用引用

默认参数

1
2
3
4
5
6
int add(int a, int b = 1)
{
return a + b;
}

cout << add(1) << " " << add(1, 3) << endl; //2 4

函数重载

函数多态是C++在C语言的基础上新增的功能。

  • 默认参数使得能够使用不同数目的参数调用同一个函数
  • 函数多态(函数重载)让您能够使用多个同名的函数

如果两个函数的参数数目类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的。 C++允许定义名称相同的函数,条件是它们的特征标不同。如果参数数目(和/或)参数类型不同,则特征标也不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void add(double a, double b)
{
cout << "double: " << (a + b) << endl;
}

void add(long a, long b)
{
cout << "long: " << (a + b) << endl;
}

void add(int a, int b)
{
cout << "int: " << (a + b) << endl;
}

void add(int a, double b)
{
cout << "int double: " << (a + b) << endl;
}

void add(int a, long b, double c)
{
cout << "int long double: " << (a + b + c) << endl;
}

add(1.0, 2.0);
add(1, 2);
add(1L, 2L);
add(1, 2.0);
add(1, 2L, 3.0);

运行结果

1
2
3
4
5
double: 3
int: 3
long: 3
int double: 3
int long double: 6

函数模板

需要多个对不同类型使用同一种算法的函数时,可使用模板

1
2
template <typename AnyType>
template <class AnyType>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template <typename T> 
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}

int i = 10;
int j = 20;
Swap(i, j);
cout << i << " " << j << endl;

double x = 24.5;
double y = 81.7;
Swap(x, y);
cout << x << " " << y << endl;

输出结果

1
2
20 10
81.7 24.5

参考链接

  1. 《C++ Primer Plus》