三、重学C++—CPP基础
上一章节:
二、重学C++—C语言核心-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/146191640?spm=1001.2014.3001.5501
本章代码:
cpp · CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/tree/master/cpp
一、引言
接下来开启 C++ 的学习旅程,这里有一些基础概念看似简单,实则蕴含着诸多重要的特性和应用场景。今天我们就来深入探讨一下 “引用” 和 “结构体” 这两个知识点,特别是将 C++ 中的结构体与 C 语言中的进行对比,帮助大家更好地理解。
二、神奇的引用
1、引用的概念
在 C++ 里,引用就像是一个 “别名”。想象一下,你有一个朋友叫小明,他同时还有个昵称叫 “阿明”,不管你喊 “小明” 还是 “阿明”,回应的都是同一个人。在代码中,变量的引用就是给这个变量另取一个名字,它们指向的是同一块内存空间。
比如我们定义一个整数变量 num,然后为它创建一个引用 ref_num:
int num = 10;
int& ref_num = num;
这里引用也是指向一块明确的内存空间,因为这里在定义“引用”时,必须明确其对象。在描述内存空间上是与“指针”类似。
这里 ref_num 就是 num 的引用,对 ref_num 进行操作,就等同于对 num 操作。比如修改 ref_num 的值:
ref_num = 20;
printf("num = %d, ref_num = %d\n", num, ref_num); // 这里两个值均发生改变
运行结果:
引用的一个重要用途是在函数参数传递中。传统的值传递会创建一个新的副本,占用额外的内存空间。而使用引用传递,就可以直接操作原始数据,提高效率,同时也能方便函数修改传入的参数值。
”前面提到引用使用跟原来数据共用同一个内存的数据,那么指针和引用有什么异同,各自使用场景是什么样?“
2、引用/指针
相同点
- 间接访问数据:引用和指针都能实现对数据的间接访问。引用是变量的别名,对引用的操作等同于对原变量操作;指针存储的是变量的地址,通过解引用操作(*)能访问指针所指向的变量。
- 用于函数参数传递:两者都能在函数参数传递时,避免对大对象进行值传递所带来的开销。使用引用传递或指针传递,函数可以直接操作原始数据,提高程序效率。
不同点
- 定义和初始化:引用在定义时必须初始化,且初始化后不能再引用其他变量,例如 int num = 10; int& ref_num = num; 而指针定义时可以不初始化,初始化后也能重新指向其他同类型变量,如 int num = 10; int *ptr = # 之后还能让 ptr 指向其他 int 类型变量;指针在使用时可以定义多级指针,但是引用使用时,无多级的使用方式;
- 语法和操作:引用的使用就像普通变量一样,直接操作即可;而指针操作时,需要通过 * 进行解引用访问指向的数据,通过 & 取变量地址。比如修改值,引用是 ref_num = 20; ,指针则是 *ptr = 20; 。
- 空值处理:引用不能为 nullptr ,因为它必须指向一个有效的对象。而指针可以被赋值为 nullptr ,表示不指向任何对象,在使用指针前通常需要检查是否为 nullptr ,避免空指针错误。
- 内存占用:一般情况下,引用本身不占用额外内存(仅作为别名),而指针变量本身需要占用一定内存空间(通常为 4 字节或 8 字节,取决于系统的寻址空间),用于存储所指向变量的地址。
优势场景
- 引用的优势场景
- 函数参数传递:当希望函数能够修改传入的参数值,同时又不想在语法上显得过于复杂时,引用传递是很好的选择。比如 void swap(int& a, int& b) 实现两个整数的交换,简洁直观。
- 性能差异:引用传递的变量本体内存,使用时直接只用该变量,但是指针使用时存在解引用的操作,需要映射到具体内存中,在绝大数场景下两者差异不明显,但是在两者都能使用的时候建议使用引用。
- 指针的优势场景
- 动态内存分配:在需要动态分配内存(如使用 new 操作符)时,必须使用指针来管理这些动态分配的内存。例如 int *arr = new int[10]; 动态创建一个包含 10 个整数的数组。
- 数据结构实现:在实现链表、树等复杂数据结构时,指针用于表示节点之间的连接关系。例如链表节点定义 struct ListNode { int val; ListNode *next; }; ,next 指针指向下一个节点。
- 灵活的地址操作:当需要进行底层的内存地址操作,或者实现一些复杂的算法(如内存池管理)时,指针能提供更灵活的控制能力。
#include <stdio.h>
int addResut(int &a, int &b)
{
++a;
++b;
return a+b;
}
int addResult1(int *a, int *b)
{
++(*a);
++(*b);
return *a+*b;
}
void createInt(int **p)
{
// new 就像是一个 “内存采购助手”,专门帮你从计算机的内存 “仓库” 中获取存储数据的空间,
// 这里需要代码编写者自己维护这块内存,如果反复申请,不去释放,就会造成内存的不可控即“内存泄漏”
*p = new int(4);
}
void createInt1(int*& p)
{
p = new int(8);
}
int main()
{
{
// 引用
int num = 10;
int& ref_num = num; // 引用时必须要赋值,如int& ref_num; 这样就是错误的使用方式
ref_num = 20;
printf("num = %d, ref_num = %d\n", num, ref_num); // 这里两个值均发生改变
// int&& ref_refNum = num; 错误
int a = 4, b = 8;
int ret = addResut(a, b);
printf("a = %d, b = %d, ret = %d\n", a, b, ret); // 这里a, b两个值均发生改变
int ret1 = addResult1(&a, &b);
printf("a = %d, b = %d, ret1 = %d\n", a, b, ret1); // 这里a, b两个值均发生改变
int *p = nullptr;
createInt(&p); // 这里指针作为变量,用于创建一个int对象
printf("*p = %d\n", *p);
int *p1 = nullptr;
createInt1(p1); // 这里传引用,指针作为一个变量,引用为这块内存的别名。
printf("*p1 = %d\n", *p1);
// delete:与new是一对一使用的,delete 来 “归还” 给内存 “仓库”
delete p;
p = nullptr;
delete p1;
p1 = p1;
}
return 0;
}
三、结构体
C++ 中的结构体在本质上和 C 语言类似,也是用于组合不同类型的数据。但 C++ 中的结构体功能更强大,它可以包含成员函数,这是 C 语言结构体所不具备的。
struct Student {
char name[20];
int age;
float score;
Student(){}
Student(char* pNm, int iAge, float fScore)
{
strcpy(name, pNm);
age = iAge;
score = fScore;
}
void printInfo() {
std::cout << "姓名: " << name << ", 年龄: " << age << ", 成绩: " << score << std::endl;
}
};
使用方式:
// 与C语言类似的使用方式
struct Student stu;
strcpy(stu.name, "李四");
stu.age = 19;
stu.score = 90.0;
stu.printInfo();
// C++ 中使用方式
struct Student stu1("Tom", 18, 99);
stu1.printInfo();
此外,在 C++ 中,结构体和类的语法非常相似,区别在于结构体默认成员访问权限是 public(公共的),而类默认是 private(私有的)。
四、总结
C++ 中的引用为我们提供了一种更灵活高效的数据操作方式,而结构体在 C 和 C++ 中既有传承又有发展,C++ 中的结构体拥有更多面向对象的特性。理解这些基础概念,对于深入学习 C++ 语言以及后续的项目开发都有着重要的意义。