缺省参数
概念
缺省参数 (又称默认参数)是指在函数声明或定义时为形参指定一个默认值,当调用函数时,如果调用者没有提供该参数的实际值,编译器会自动使用预先设定的默认值
缺省函数的使用
cpp
#include<iostream>
using namespace std;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func();
Func(10);
return 0;
}
第一个func函数没有传参,所以就会调用缺省值,第二个func函数传10,就会调用传递的值

全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续给予缺省值 ,不能间隔跳跃给缺省值
cpp
#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 << "\n" << endl;
}
// 半缺省
void Func2(int a, int b = 10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << "\n" << endl;
}
int main()
{
Func1();
Func1(1);
Func1(1, 2);
Func1(1, 2, 3);
Func2(100);
Func2(100, 200);
Func2(100, 200, 300);
return 0;
}

缺省函数的好处
允许调用者只传递必要的参数,省略的参数自动使用默认值,使代码更简洁
避免为不同参数组合编写多个重载函数,一个函数即可覆盖多种调用场景
函数可以同时满足通用需求和特殊需求:大多数情况使用默认值,特殊需求时显式传参。
为参数设定常用默认值,降低使用门槛,提高代码的可读性和易用性。
新增参数时,只要设为默认值,原有调用代码无需修改,保持向后兼容
在类构造函数中使用默认参数,可以方便地提供多种对象创建方式
想象一下:你妈叫你吃饭,只说"快过来",你就自动知道要带筷子、坐老位置、先喝汤------这就是缺省参数!它让函数在调用者"偷懒"的时候,自己把缺的参数补上,堪称编程界的"自动脑补侠"
函数重载
概念
想象一下,你有个朋友叫"小明",但他同时会修电脑、会做饭、会修马桶------每次你喊"小明过来!",他就会根据你当时的需求自动切换技能:你说"电脑卡了",他变程序员;你说"肚子饿了",他变厨师。这就是函数重载
函数重载允许你在同一个作用域里,定义多个同名函数 ,只要它们的参数列表不同,编译器会根据你调用时传的实参,自动派发对应的函数去执行
函数重载的使用
cpp
#include<iostream>
using namespace std;
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
void func3()
{
cout << "func3:" << endl;
}
void func4(int a)
{
cout << "func4:"<< a << endl;
}
int main()
{
int ret1=Add(1, 2);
double ret2=Add(1.1, 2.2);
cout << ret1 << endl;
cout << ret2 << endl;
func3();
func4(100);
return 0;
}

所以我们发现,只要形参不同,编译器就能自己识别不同函数,但是返回值不同,不能做为重载条件
cpp
void func(int a)
{
cout << "func(int a)";
}
int func(int a)
{
cout << "func(double a)";
}
int main()
{
func(1);
return 0;
}

cpp
void func()
{
cout << "func()";
}
void func(int a=10)
{
cout << "func(int a)";
}
int main()
{
func();
return 0;
}

这是因为我们定义了两个func函数,一个没有形参,一个有缺省值,我的函数既可以匹配没有形参的func,也可以匹配有缺省值的函数,这样的话编译器就不知道该匹配哪一个函数
引用
概念
引用 就是给一个已经存在的变量取一个别名,它俩共用同一块内存空间,你对引用做的任何操作,都会直接作用在原变量身上------相当于给同一个人起了两个名字:你喊"张大帅"和喊"二狗子",都是同一个人回头
cpp
int main()
{
int a = 0;
//引用:b和c是a的别名
int& b = a;
int& c = a;
int& d = b;
++d;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
这是不是就证明了引用的地址和变量的地址在同一块内存中

那我们再来看一下值有什么变化
cpp
int main()
{
int a = 0;
//引用:b和c是a的别名
int& b = a;
int& c = a;
int& d = b;
++d;
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << d << endl;
return 0;
}
我们最先定义的a的值是0,现在就全都变为1了

引用的特点
引用在定义时必须初始化
cpp
int main()
{
int a = 0;
int& ra;
return 0;
}

引用一但引用一个实体,就不能再引用其他实体
cpp
int main()
{
int a = 0;
int& b=a;
int c = 1;
b = c;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
return 0;
}

当程序执行到"b=a"的时候,可能朋友们就会说让 b 引用 c,大错特错, 引用一旦绑定,终身无法改嫁, b 从出生那一刻就死心塌地跟着 a,这一行 b = c 的真实含义是:**把 c 的值赋给 b 所引用的变量(也就是 a),**从地址上我们也可以看出来,b和c的地址是不一样的
引用的使用
之前大家都写学习过地址,都学过传值调用 和**传址调用,**如果要改变值就要使用传址调用,而现在我们已经学了引用,那我们就可以用引用来替代传址调用
cpp
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;
return 0;
}

这里rx是x的别名,ry是y的别名,所以这里我们就不用去调用地址,直接引用就可以了
const对象的引用
权限放大可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象 ,因为对象的访问权限 在引用过程中可以缩小 ,但是不能放大
cpp
int main()
{
const int a = 10;
//权限放大
int& b = a;
return 0;
}
这就是我们的权限放大,当整型int被const修饰时说明该变量只能读取不能改写 ,如果该变量没有被const修饰那这个变量既可读也可写,权限只能缩小不能放大 ,但是b却没有错误,说明这个时候b是既可读也可写 ,b是a的别名,b就从只能读取 放大到了既可读也可写 ,这就是权限放大

那我们想要a和b平起平坐,那我们就需要使用到const
cpp
int main()
{
//放大权限
const int a = 10;
const int& b = a;
return 0;
}
cpp
int main()
{
//缩小权限
int a = 10;
const int& b = a;
return 0;
}

给常量取别名需要用const修饰

cpp
int main()
{
double e = 12.34;
int i = e;
const int& ri = e;
return 0;
}
e给int的时候会产生隐式转换 ,而隐式转换会产生临时对象(还有表达式相加),把d的整数部分拿出来放在临时对象,ri引用临时对象,而临时对象具有**常性,**需要用const修饰
指针和引用的关系
- 本质:指针是存地址的独立变量,引用是变量别名
- 初始化:指针可延迟初始化 / 置空,引用必须绑定变量且不可改指向
- 访问:指针需 * 解引用,引用直接使用
- 空值:指针可空(易出野指针),引用不能为空(更安全)
- 灵活性:指针支持多级 / 改指向,引用功能受限但语法简洁
- 场景:函数参数 / 返回值优先用引用,需空值 / 改指向用指针
nullptr
NULL 实际是一个宏,在传统的C头文件 (stddef.h)中
cpp
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
在C++中NULL被定义为字面常量0,而在C中被定义为无类型指针 (void*) 的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,本想通过 f(NULL) 调用指针版本的 f(int*) 函数,但是由于NULL被定义成0,变成了调用了 f(int x) ,因此与程序的初衷相悖。而只有将 NULL 类型强转成 int* 才可实现
cpp
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
//f((void*)NULL);编译错误
return 0;
}

而到了C++11后就引入了nullptr,nullptr是一个特殊的关键字,nullptr是一种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型
cpp
void f(int x)
{
cout << "f(int x)" << endl;
}
void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
int main()
{
f(0);
f(NULL);
f(nullptr);
return 0;
}

所以以后我们就不再用NULL表示空指针了,而是nullptr