avatar

Catalog
从8开始的C++世界生活(引用、new&delete)

本篇是学习VC驿站的《实用C++》教程时的超简化(不太会的部分会写多一点)同步笔记,详细内容可参见VC驿站原帖

是在学校C++课程之外重新系统地学习一遍C++,所以很基础很基础的部分不会记录

代码块渲染有误,目前不知道怎么解决,#include后面的总是消失,所以#include统一写作包含,懂什么意思就行了

如果觉得网站字体太小,可以在右下方的按钮中点击+放大哦√(比如我就觉得这个网站的字体很小)

希望对你和我都能有所帮助 : )

引用

概念

引用C++对于C语言的一个重要的扩充。C语言中没有引用,C++有引用,而且C++中更建议大家多用引用少用指针。

变量的引用就是一个变量的别名,变量和变量的引用代表着同一个变量。例如:
int a = 5; //语句1
int& b = a; //语句2
int* p = &a; //语句3
这里面a是一个普通的int类型变量,b是变量a的一个引用,p呢就是指向变量a地址的一个指针变量。
其中语句2中的 & 符号是引用的声明符号不是取地址,语句3中的 & 符号是取地址符。
如何来区分呢?大家记住:紧跟在数据类型后面的&符号就是引用的声明符号,其他情况都可以认为是取地址符号。

注意事项

  1. 引用不是一种独立的数据类型,引用只有声明,没有定义。必须先定义一个变量,之后对该变量建立一个引用。也就是说有变量才有变量的引用,不可能先声明一个引用而不去引用任何变量。

    这点跟指针不同,指针可以先声明,之后的任意时刻指向某个变量的地址,引用不可以。
    例如:int &b; //先声明定义一个引用是错误的

  2. 声明一个引用时,必须同时对其初始化,即声明该引用代表哪一个变量。这个跟第①点要表达的意思一样。

    有一种例外的情况,当一个函数的参数是某个变量的引用时,形参不必在声明中初始化,他的初始化是在函数调用时的虚实结合实现的,即作为形参的引用是实参的别名;
    void swap(int& a, int& b);

  3. 声明一个引用后,不能再让其作为另一个变量的引用了。例如:
    int a1 = 2, a2 = 5;
    int& b = a1; //正确
    int& b = a2; //错误

    c++
    1
    2
    3
    4
    int a1 = 2, a2 = 5;
    int& b = a1;
    b = a2;
    cout << b;

    刚刚突发奇想,如果代码这么写能否实现让b作为a2的引用?

    结果输出是5,看起来好像是可以的。

    但是在监视窗口发现,这样做,a1,a2,b都为5。

    也就是说,b = a2这一步,看起来好像是让b作为a2的引用了,但是实际上,这一步相当于a1 = a2,最后cout << b,相当于cout << a1,所以输出才会是5。对这三个变量取地址,a1与b的地址相同,a2的地址不同。

    综上,声明一个引用后,不能再让其作为另一个变量的引用了。

  4. 不能建立引用数组,例如:
    int a[5] = {0};
    int& b[5] = a; //错误

    这样子好像是违反了第1点,只有第一个元素被初始化了

    编译器会报错:引用数组是非法的。

    int& c = a[0]; //正确(C++新标准支持)

  5. 可以建立引用的引用(C++新标准支持),也可以建立引用的指针,例如:
    int a = 3;
    int& b = a; //正确
    int& c = b; //正确
    int* p = &b; //正确,得到的是变量a的地址
    *p = 5;
    c = 6;

    输出a的值为6

和指针的联系

关于引用的性质,如果在程序中声明了b是变量a的引用,实际上在内存中为b开辟了一个指针型的存储单元,在其中存放变量a的地址,输出引用b时,就输出b所指向的变量a的值,相当于输出*b。引用其实就是一个指针常量,他的指向不能改变,只能指向一个指定的变量。所以,引用的本质还是指针,所有引用的功能都可以由指针实现。C++之所以增加引用的机制,是为了方便用户,用户可以不必具体去处理地址,而把引用作为变量的“别名”来理解和使用,而把地址的细节隐藏起来,这样难度会小一些。

不用指针,用引用的方式实现 swap 函数,功能是交换两个整形变量的值,实现如下:
void swap(int& a, int& b)
{
int t = a;
a = b;
b = a;
}

new&delete

概述

在以后的开发过程中,因为局部变量的局限性,只能在其作用域内使用。因此,我们需要动态的分配和撤销内存空间,使其可以在任何函数中使用。

例如:
char* get_same_string(char* p1, char* p2)
{
//
}
get_same_string 函数的作用是从参数p1和p2中找出相同的部分,例如,p1的内容是:”aabbcc”,p2的内容是:”kkbcyy”,他们相同的子串就是 “bc” 。我想把这个结果通过函数的返回值给传出去,所以函数的返回值是一个 char* 类型。如果在函数中定义一个局部变量 szret[100] 数组,用这个数组来存储相同部分的子串 “bc”,那么就不能返回,为什么呢?因为 szret 是局部变量,作用域只是在函数的内部,超过函数的作用域之后 szret 的内存就可能被释放了(返回的指针指了个寂寞)。所以用它来返回之后,在函数的外部再去使用是非常不安全的,也是错误的。所以这种情况就可以使用 new 动态分配内存来解决。

new 出来的变量/内存的生命周期

C++ 中的 new操作符 和C语言中的 malloc 函数类似,如果你不主动 delete 掉这段申请的内存的话,它会一直存在,直到进程结束后系统会回收掉这段资源;而如果你delete掉这段申请的内存,则这段申请到的内存的生命周期为从你new(申请一段内存)到你delete(释放掉这段内存)这段时间。

使用方式

C语言中是使用 malloc 和 free 两个函数来进行动态内存的申请和释放的,C++用引入了更为智能的 new 和 delete 操作符来进行内存的申请和释放,举例:

c++
1
2
3
int* p = (int*)malloc(sizeof(int)); //C语言中使用 malloc 来申请一个int类型变量的内存
*p = 5;
free(p);
c++
1
2
3
4
5
int* p = new int(5); //C++ 中使用 new 来申请一个int类型变量的内存
//()中放初始化的值,如果是初始化结构体类型变量,则用{}
delete p; //删除变量

//刚在网上查到的一句话:在删除一个指针之后,一定将该指针设置成空指针(即在delete *p之后一定要加上: p=NULL)
c++
1
2
3
4
int* p = new int[5]; //使用new申请一个包含5个int元素的数组
p[0] = 2;
p[1] = 3;
delete [] p; //删除数组

还有一个更重要的new优于malloc的地方:

在C++中的类class,用new申请一个类对象的时候,对象申请成功之后会默认调用其构造函数;而C语言中的malloc只是会申请空间,但是不会调用对象的构造函数。所以,在C++中大家就放弃 malloc 和 free 吧,直接用 new 和 delete 来操作动态内存。

小作业

完成 get_same_string 函数的功能。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
char* get_same_string(char* p1, char* p2)
{
int judge(int i, int j, char* p, char* p1, char* p2);
int flag = 0;
char* p = new char[100];
for (int i = 0; p1[i] != 0; i++)
{
for (int j = 0; p2[j] != 0; j++)
{
if (p1[i] == p2[j])
{
flag = judge(i, j, p, p1, p2);
}
if (flag == 1)
{
break;
}
}
if (flag == 1)
{
break;
}
}
return p;
}

int judge(int i, int j, char* p, char* p1, char* p2)
{
int k;
if (p1[i+1] != p2[j+1])
{
return 0;
}
else
{
p[0] = p1[i];
for (k = 1; p1[++i] == p2[++j]; k++)
{
p[k] = p1[i];
}
}
p[k] = 0;
return 1;
}

int main()
{
char a[] = "aabbcc";
char b[] = "kkbcyy";
char* qwq = get_same_string(a, b);
cout << qwq;
delete qwq;
}

逻辑:找到相同字母后,判断之后的字符是否相同,如果相同就找到了这个子串,存储到相应位置中。

所以这段代码使用的条件:只有一个子串;单个字符不算子串。

细节:一开始我写的是cout << *qwq; 这是取了数组第一个元素的值,所以只输出b。要输出整个字符数组即字符串,应当是cout << qwq;

解这道题应该还有更好的解法以适用于更多情况,aaaaa暂时想不出来。

Author: Crux
Link: http://cruxssssss.github.io/2020/06/21/cong8/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶