当前位置: 首页 > news >正文

C++:入门基础(2)

本章代码见:https://gitee.com/jxxx404/cpp-language-learning/commit/ff688eaf2475edd2485100b9295f20bfaf817dda

上一章文章:https://blog.csdn.net/2401_86123468/article/details/151836484?spm=1001.2014.3001.5501

大略复习上一章文章:注意using namespace std;的使用,在算法竞赛中换行推荐使用'\n',若使用endl;会更新缓冲区,影响效率。以及在竞赛中可以先添加下面三行代码:

#include<iostream>
using namespace std;int main()
{// 在io需求比较高的地方,如部分大量输入的竞赛题中,加上以下3行代码// 可以提高C++IO效率ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);return 0;
}

1.缺省参数

1.1

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把缺省参数也叫默认参数

1.2

全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

1.3

带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。

1.4

函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。

#include<iostream>
using namespace std;void Func(int a = 0)//赋予参数值,此为缺省值,a为缺省参数
{cout << a << endl;
}int main()
{Func(); // 没有传参时,使⽤参数的默认值 Func(10); // 传参时,使⽤指定的实参 return 0;
}
得到的结果:
0
10

全缺省:

#include<iostream>
using namespace std;
// 全缺省
void Func1(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}int main()
{Func1(1, 2, 3);Func1(1, 2);Func1(1);Func1();return 0;
}得到的结果:
a = 1
b = 2
c = 3a = 1
b = 2
c = 30a = 1
b = 20
c = 30a = 10
b = 20
c = 30

半缺省:

#include<iostream>
using namespace std;
// 半缺省
void Func2(int b, int c = 20, int a = 10)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}int main()
{Func2(1);Func2(1, 2);Func2(1, 2, 3);return 0;
}
得到的结果:
a = 10
b = 1
c = 20a = 10
b = 1
c = 2a = 3
b = 1
c = 2
// Stack.h
#include <iostream>
#include <assert.h>
using namespace std;
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;
void STInit(ST* ps, int n = 4);// Stack.cpp
#include"Stack.h"
// 缺省参数不能声明和定义同时给 
void STInit(ST* ps, int n)//在定义时要写好
{assert(ps && n > 0);ps->a = (STDataType*)malloc(n * sizeof(STDataType));ps->top = 0;ps->capacity = n;
}// test.cpp
#include"Stack.h"
int main()
{ST s1;STInit(&s1);// 确定知道要插⼊1000个数据,初始化时⼀把开好,避免扩容 ST s2;STInit(&s2, 1000);return 0;
}

2.函数重载

C++支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调用就表现出了多态行为,使用更灵活。C语言是不支持同一作用域中出现同名函数的。

2.1参数类型不同

#include<iostream>
using namespace std;int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}int main()
{cout << Add(1, 2) << endl;cout << Add(1.1, 2.2) << endl;return 0;
}结果为:
int Add(int left, int right)
3
double Add(double left, double right)
3.3

2.2参数个数不同

#include<iostream>
using namespace std;void f()
{cout << "f()" << endl;
}
void f(int a)//1个int参数
{cout << "f(int a)" << endl;
}int main()
{f();f(10);return 0;
}结果为:
f()
f(int a)

2.3参数类型顺序不同

#include<iostream>
using namespace std;void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}int main() {f(10, 'A');   // 调用参数为「int, char」的版本f('B', 20);   // 调用参数为「char, int」的版本return 0;
}
结果为:
f(int a,char b)
f(char b, int a)

注意:

1.返回值不同不能作为重载条件,因为调用时无法区分 

#include<iostream>
using namespace std;// 返回值不同不能作为重载条件,因为调⽤时也⽆法区分 
void fxx()
{return 0;
}
int fxx()
{return 0;
}int main()
{// 调用时无法确定要调用哪个fxx();return 0;
}

2.下面两个函数构成重载,但是不传参调用时,存在调用歧义。

//此时是错误代码
#include<iostream>
using namespace std;void f1()
{cout << "f()" << endl;
}void f1(int a = 10)
{cout << "f(int a)" << endl;
}
int main()
{//f1(1);f1();
}

3.引用

3.1引用的概念和定义

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间
它和它引用的变量共用同一块内存空间

类型&引用别名=引用对象;

#include<iostream>
using namespace std;int main()
{int a = 0;// 引⽤:b和c是a的别名 int& b = a;int& c = a;// 也可以给别名b取别名,d相当于还是a的别名 int& d = b;++d;// 这⾥取地址我们看到是⼀样的 cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;return 0;
}
结果为:
000000D9471BF854
000000D9471BF854
000000D9471BF854
000000D9471BF854

3.2引用的特性

1.引用在定义时必须初始化
2.一个变量可以有多个引用
3.引用一旦引用一个实体,再不能引用其他实体

#include<iostream>
using namespace std;int main()
{int i = 1;int& j = i;cout << &i << endl;cout << &j << endl;++j;// 一个变量可以有多个引用int& k = j;k = 10;// 引用在定义时必须初始化/*int& x;x = i;*/// 引用一旦引用一个实体,再不能引用其他实体int m = 20;k = m;//此时是赋值return 0;
}

3.3引用的使用

1.

引用在实践中主要是于引用传参传引用返回减少拷贝(深拷贝)提高效率改变引用对象时同时改变被引用对象。

2.

引用传参跟指针传参功能是类似的,引用传参相对更方便一些。

#include<iostream>
using namespace std;// 指针
// 引用
// 大部分场景去替代指针,部分场景还是离不开指针void Swap(int* rx, int* ry)
{int tmp = *rx;*rx = *ry;*ry = tmp;
}void Swap(int& rx, int& ry)
{int tmp = rx;rx = ry;ry = tmp;
}int main()
{int x = 0, y = 1;cout << x << " " << y << endl;// Swap(&x, &y);cout << x << " " << y << endl;Swap(x, y);cout << x << " " << y << endl;return 0;
}结果为:
0 1
0 1
1 0

也可以这样:

typedef struct SeqList
{//...
}SL;//void SLInit(SL* psl, int n = 4)
void SLInit(SL& psl, int n = 4)
{//...
}

对于二级指针,可以这样:

void Swap(int** pp1, int** pp2)
{int* tmp = *pp1;*pp1 = *pp2;*pp2 = tmp;
}void Swap(int*& rp1, int*& rp2)
{int* tmp = rp1;rp1 = rp2;rp2 = tmp;
}
int main()
{int x = 0, y = 1;cout <<x<<!<< y << endl;Swap(&x, &y);cout << x<<<< y << endl;Swap(x, y);cout <<x<<""<< y << endl;SL s;SLInit(s) ;int* pl = &x, * p2 = &y;Swap(&p1, &p2);Swap(pl, p2);//区别
}

3.传引用返回

引用返回值的场景相对比较复杂,这里先只简单讲一下场景。

错误做法:

注意下方演示:若返回局部变量的引用,会因局部变量生命周期结束导致 “野引用”,是错误用法。

以及下面的代码,即使在return ret后被销毁,但仍可能结果相同。

因为局部变量的生命周期仅限于函数执行期间:当函数返回后,其栈帧(包含局部变量的内存)会被 “释放”(不再受程序控制),但内存空间本身不会立即 “清空”,只是标记为 “可复用”。此时,main 中的 x 作为引用,指向的是一块已释放的栈内存(野引用),后续对 x 的访问属于未定义行为(结果不可预测)。

int& func1()
{int ret = 0;// ...return ret;
}int& func2()
{int y = 456;// ...return y;
}int main()
{int& x = func1();cout << x << endl;func2();cout << x << endl;return 0;
}
正确做法:加static。
int& func1()
{static int ret = 0;// ...return ret;
}int& func2()
{int y = 456;// ...return y;
}int main()
{int& x = func1();cout << x << endl;func2();cout << x << endl;return 0;
}

通过修改顺序表第i个位置的值,了解引用的特性:语法简洁、无需解引用、必须初始化,以及它与指针在内存和使用上的不同。

指针写法:

#include"SeqList.h"int main()
{// 1. 定义顺序表对象s,并初始化(初始容量为10)SL s;SLInit(&s, 10);  // 初始化顺序表,分配初始空间// 2. 向顺序表尾部插入10个元素(0~9)for (size_t i = 0; i < 10; i++){SLPushBack(&s, i);  // 尾插操作:将i插入到顺序表s的末尾}// 3. 打印顺序表中的所有元素for (size_t i = 0; i < 10; i++){// SLat函数:获取顺序表s中第i个位置的元素cout << SLat(&s, i) << " ";  }cout << endl;  // 输出结果:0 1 2 3 4 5 6 7 8 9// 4. 修改顺序表中指定位置的元素int i = 0, x = 0;cin >> i;  // 输入要修改的位置(假设输入3)cin >> x;  // 输入新值(假设输入100)// 关键操作:通过SLat函数的返回值直接修改元素// 前提:SLat函数的返回类型必须是int&(元素的引用)SLat(&s, i) = x;  // 等价于修改顺序表第i个元素的值为x// 5. 再次打印顺序表,验证修改结果for (size_t i = 0; i < 10; i++){cout << SLat(&s, i) << " ";  }cout << endl;  // 若输入i=3、x=100,输出:0 1 2 100 4 5 6 7 8 9return 0;
}

引用写法:

#include<iostream>
using namespace std;int main()
{int i = 0;  // 定义int变量i,初始值为0// 1. 引用的定义:r1是i的别名(必须初始化,且绑定后不可更改指向)// 引用不占用额外内存空间,与i共享同一块内存int& r1 = i;  // 2. 指针的定义:p存储i的地址(可以先定义后初始化,可更改指向)// 指针本身占用内存(32位系统4字节,64位系统8字节)int* p = &i;  // 3. 通过引用操作原始变量ir1++;  // 等价于i++,i的值变为1// 4. 通过指针操作原始变量i(*p)++;  // 指针需解引用(*)才能访问i,等价于i++,i的值变为2return 0;  // 最终i的值为2
}

4.

引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引用(如Java)是有很大的区别的,除了用法,最大的点,C++引用定义后不能改变指向,
Java的引用可以改变指向。

5.

一些主要用C代码实现版本数据结构教材中,使用C++引用替代指针传参,目的是简化程序,避开复杂的指针。

void SeqPushBack(SLT& sl, int x)
{//...
}typedef struct ListNode
{int val;struct ListNode* next;
}LTNode, *PNode;// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名 
// 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序 
//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)void ListPushBack(PNode& phead, int x)
{PNode newnode = (PNode)malloc(sizeof(LTNode));newnode->val = x;newnode->next = NULL;if (phead == NULL){phead = newnode;}else{//...}
}int main()
{PNode plist = NULL;ListPushBack(plist, 1);return 0;
}

有些C语言书上是运用了引用的知识

typedef struct SListNode
{int val;struct SListNode* next;
}SLTNode,* PSLTNode;

具体这样理解更清楚:

本章完。

http://www.dtcms.com/a/393391.html

相关文章:

  • 数据架构章节考试考点及关系梳理
  • 用TRAE编程助手编写一个浏览器插件
  • 赋能工业未来:向成电子XC3576H工控主板多领域应用前景
  • Multi-Agent多智能体系统(三)
  • 【语法进阶】高级用法、贪婪与非贪婪
  • 15天见效的SEO优化方案
  • C语言基础【20】:指针7
  • IC 数字逻辑设计中的硬件算法 01 记
  • 《棒球运动联盟》国家级运动健将标准·棒球1号位
  • AAC 详解
  • 蚂蚁集团DIVER登顶BRIGHT榜首,开源多阶段推理检索范式
  • 2013/12 JLPT听力原文 问题四
  • 挑战与应对:轻量化 AI 算法的成长烦恼
  • FPGA基础 -- CDC(Clock Domain Crossing)实战教程
  • 低碳经济:碳汇——从生态固碳到金融资产的价值转化
  • QGC 通信模块架构梳理
  • Application接口拓展功能(三)
  • 【Python】错误和异常
  • 【状态机实现】初识——基于状态机实现的流程编排和Activiti、Camunda、Flowable等工作流的区别
  • SpringBoot自动配置核心原理
  • Python 中的 Builder 模式实践 —— 以 UserProfileBuilder 为例
  • 探秘陌讯AIGC检测算法优化:详解MPS加速与模型热重载的实现原理
  • 1.3 管道(Pipe)核心知识点总结
  • GLUE:自然语言理解评估的黄金基准
  • 第13章 智能监测-设备数据处理
  • GEO技术科普
  • B004基于三菱FX2NPLC智能自提柜控制系统仿真
  • MTK CPU温度调节一知半解
  • V90伺服驱动器“速度模式“双极性模拟量速度控制
  • 课前练习题-20250919