c++远征02离港篇

C++远征之离港篇-慕课网

1 离港总动员

  • 指针 vs 引用
  • define VS const
  • 函数默认值 & 函数重载
  • 内存管理类(出来混,总是要还的…)

2 c++ 语言引用

  • 引用是变量的别名
  • 引用不能单独存在,只能对存在的变量声明引用

2.1 基本使用

说明:对变量的引用的操作等价于对变量本身进行操作。

基本类型的引用

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

int main(void) {
int a = 3;
int &b = a; // 引用声明时必须初始化(类似 const)

b = 10;
cout << a << endl; // 10
return 0;
}

结构体类型的引用

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
#include <iostream>
using namespace std;

// 坐标
typedef struct
{
int x;
int y;
} Coor;

int main(void)
{

Coor c1;
// 定义引用
Coor &c = c1;

// 通过引用操作结构体实例
c.x = 10;
c.y = 20;

// 直接访问结构体本身实例
cout << c1.x << ", " << c1.y << endl; // 10, 20

return 0;
}

指针类型的引用
类型 *&指针引用名 = 指针;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

int main(void)
{

int a = 10;
int *p = &a; // 声明指针
int *&q = p; // 声明引用 q
*q = 20; // 通过引用修改 a 的值(用法和普通指针相同)

cout << a << endl; // 20

return 0;
}

2.2 引用作为函数实参

说明: 在处理诸如交换两个变量的值之类的问题时,引用指针的实现更简单。

交换两个变量的值

方式一:应用指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

// 交换两个整数的值(有副作用)
void swap(int *a, int *b)
{

int c = 0;
c = *a;
*a = *b;
*b = c;
}

int main(void)
{

int x = 10, y = 20;
swap(&x, &y);

cout << "x = " << x << ", " << "y = " << y << endl; // x = 20, y = 10
}

方式二:应用引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

// 交换两个整数的值(有副作用)
void swap(int &a, int &b)
{

int c = 0;
c = a;
a = b;
b = c;
}

int main(void)
{

int x = 10, y = 20;
swap(x, y);

cout << "x = " << x << ", " << "y = " << y << endl; // x = 20, y = 10
}

3 c++ 语言 const 关键字

3.1 const

说明: 用来限制对值的写权限,常见用途包括

  • 提高代码的可读性
  • 防止函数对变量的修改

3.1.1 基本用法

const 与基本数据类型

说明:基本数据类型的变量使用 const ,使变量的值只读。

不使用 const 的内存模型

1
int x = 3; // 变量

变量名|存储地址|存储内容
—|—|—
x|&x|3

使用 const 的内存模型

1
const int x = 3;// 常量

变量名|存储地址|存储内容
—|—|—
x|&x|3(只读)

const 与指针类型

通过p指向内容只读

1
2
3
4
5
6
int x = 3;
// const 修饰的是 *p
const int * p = &x;

p = &y; // 正确,p 的值可以被读写
// *p = 4; (错误,*p 只读)

变量名|存储地址|存储内容
—|—|—
x|&x|3
p|&p|&x

指针 p 只读

1
2
3
int x = 3;
int * const p = &x;
// p = &y;(错误的,p 是常量)

变量名|存储地址|存储内容
—|—|—
x|&x|3
p|&p|&x(只读)

指针 p 和 通过 p 指向内容都只读

1
2
3
4
const int x = 3;
const int * const p = &x;
// p = &y; (错误,p 只读)
// *p = 4;(错误,*p 只读)

变量名|存储地址|存储内容
—|—|—
x|&x|3(只读)
p|&p|&x(只读)

const 与引用(别名)

说明: 声明别名时如果被 const 修饰,则通过别名对变量的值的访问是只读的,但不影响其它方式对该变量的访问。

1
2
3
4
int x = 3;
const int &y = x;
x = 10;
// y = 20;(错误,y 这个别名只读)

变量名|存储地址|存储内容
—|—|—
x|&x|3

3.1.2 注意点

  • 针对已有变量,声明指针或引用时,读写权限不能高于原变量。

错误示例

1
2
const int x = 3;
// int *y = &x;(错误)

正确示例

1
2
3
int x = 3;
const int *y = &x;
int *z = &y;

3.2 代码演示

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
33
#include <iostream>
#include <stdlib.h>
using namespace std;

// 通过声明 const 引用,明确限制对原数据的修改
void fun(const int &a, const int &b);

int main(void)
{

int x = 3;
int y = 5;

/* 指针本身为只读 */
int * const p = &x;
*p = 10;
cout << x << ", " << y << endl; // 10, 5
// p = &y;(错误)

/* 引用只读 */
int const &z = y;
// z = 10;(错误)

/* 在函数中的应用 */
fun(x, y);

return 0;
}

void fun(const int &a, const int &b)
{

// a = 10;(错误)
// b = 20;(错误)
}

4 c++ 函数新特性

4.1 函数特性

4.1.1 函数参数默认值

注意

  • 默认值只能在函数声明中提供(函数定义中给默认值在有的编译器中会出问题)
  • 默认值可以有一个或多个,但提供了默认值的参数,必须在参数列表的最右端
  • 没实参时则用默认值,否则实参覆盖默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
void fun(int i, int j = 0, int k = 0);

int main(void)
{

fun(20); // 20, 0, 0
fun(20, 30); // 20, 30, 0
fun(20, 30, 40); // 20, 30, 40

return 0;
}

void fun(int i, int j, int k)
{

cout << i <<", " << j << ", " << k << endl;
}

4.1.2 函数重载

条件

  • 在相同作用域
  • 用同一函数名定义的多个函数
  • 参数个数或参数类型不同
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
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
using namespace std;
/**
*函数功能:返回a和b的最大值
*a和b是两个整数
*/

int getMax(int a, int b)
{

return a > b ? a : b;
}

/**
* 函数功能:返回数组中的最大值
* arr:整型数组
* count:数组长度
* 该函数是对上面函数的重载
*/

int getMax(int *a, int len)
{

//定义一个变量并获取数组的第一个元素
int maxNum = a[0];
for(int i = 1; i < len; i++)
{
//比较变量与下一个元素的大小
if(maxNum < a[i])
{
//如果数组中的元素比maxNum大,则获取数组中的值
maxNum = a[i];
}
}
return maxNum;
}

int main(void)
{

//定义int数组并初始化
int numArr[3] = {3, 8, 6};

//自动调用int getMax(int a, int b)
cout << getMax(numArr[0], numArr[2]) << endl;

//自动调用返回数组中最大值的函数返回数组中的最大值
cout << getMax(numArr, 3) << endl;
return 0;
}

4.1.3 内联函数

说明: 相比普通函数,內联函数没有真正的调用过程,因为编译时,函数调用语句就被函数体代码和实参代替了。
关键字: inline

理解內联函数

执行流:普通函数 vs 內联函数

  • 普通函数: 1->2->3->4->5
  • 內联函数:1->3->5(相比普通函数,內联函数没有调用的开销,因此性能更好)

举一个简单的例子

预编译前的代码释义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
inline int max(int a, int b, int c);

int main(void)
{

int i = 10, j = 20, k = 30, m;
m = max(i, j, k);
cout << "max=" << m << endl;

return 0;
}

inline int max(int a, int b, int c)
{

if(b > a) a = b;
if(c > a) a = c;
return a;
}

预编译之后的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

int main(void)
{

int i = 10, j = 20, k = 30, m;
a = i; b = j; c = k;
if(b > a) a = b;
if(c > a) a = c;
m = a;
cout << "max=" << m << endl;

return 0;
}

注意点

  • 內联编译是建议性的,由编译器决定。
  • 逻辑简单(不要包含 for 循环,非递归),调用频繁的函数建议使用內联。

5 c++ 内存管理

内存管理就是指向操作系统申请或归还内存资源。

5.1 内存管理

5.1.1 内存的申请和释放

  • 申请:new 运算符
1
2
3
4
5
/* 一个单位的内存 */
int *p = new int;

/* 一块连续的块内存 */
int *arr = new int[10];
  • 释放:delete 运算符
1
2
3
delete p; // 释放一个单位的内存

delete [arr]; // 释放快内存

5.1.2 内存管理的其它方式

注意:配套使用不要混搭!

5.1.3 申请内存注意事项

  • 使用 new 申请内存,使用 delete 释放内存。
  • 申请内存需要判断是否成功,释放内存需要设置空指针。
  • new 和 delete 配套使用,不要和 c 语言的方式混搭。

内存释放后,相关变量设置为 NULL(防止不小心使用了 p 或对同一块地址重复释放。如果不设置为 NULL, p 仍然指向那块释放了的内存地址,访问会出现无法预料的情况)。

1
2
3
4
5
6
7
8
int *p = new int[1000];
if(NULL == p)
{
// 内存分配失败
// 异常处理
}
delete p;
p = NULL;
1
2
3
4
5
6
7
8
9
int *p = new int[1000];
if(NULL == p)
{
// 内存分配失败
// 异常处理
}

delete []p;
p = NULL:

5.2 代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <string.h>
#include <iostream>
using namespace std;
int main(void)
{
//在堆中申请100个char类型的内存
char *str = new char[1000];
//拷贝Hello C++字符串到分配的堆中的内存中
strcpy(str, "hello imooc");
//打印字符串
cout << str << endl;
//释放内存
delete []str; // 如果漏掉了 [], 只会释放内存块的第一个单元,导致内存泄漏
str = NULL;
return 0;
}