一、缺省参数(默认参数)

1. 什么是缺省参数?

缺省参数就是声明 / 定义函数时,为形参指定一个默认值。调用函数时,如果没传实参,就用这个默认值;传了实参,就用传入的值。 也叫 “默认参数”,分为全缺省(所有形参都给默认值)和半缺省(部分形参给默认值)。

2. 核心规则

  • 半缺省参数必须从右往左连续给,不能间隔跳跃。

  • 调用带缺省参数的函数时,实参必须从左到右依次给,不能跳跃传参。

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

示例

// 声明时给缺省值
void func(int a, int b = 10, int c = 20);

int main() {
    func(1);    // a=1, b=10, c=20
    func(1, 2); // a=1, b=2, c=20
    func(1,2,3);// a=1, b=2, c=3
    return 0;
}

// 定义时不再写缺省值
void func(int a, int b, int c) {
    // ...
}

二、函数重载

1. 什么是函数重载?

C++ 支持在同一作用域中定义同名函数,但这些函数的形参必须满足:

  • 参数个数不同,或

  • 参数类型不同(或顺序不同)。

这种特性让函数调用表现出 “多态行为”,使用更灵活。C 语言不支持同一作用域的同名函数。

示例

int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }

int main() {
    add(1, 2);     // 调用int版
    add(1.5, 2.5); // 调用double版
    add(1, 2, 3);  // 调用三参数版
    return 0;
}

三、引用的概念与用法

1. 什么是引用?

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

语法:类型& 引用别名 = 引用对象;

示例

int a = 10;
int& b = a; // b是a的别名

cout << &a << endl; // 地址和a相同
cout << &b << endl;

2. 引用的四大功能

① 做函数形参:修改实参的值
// 错误:值传递,无法交换

void swap(int p1, int p2) {
    int tmp = p1;
    p1 = p2;
    p2 = tmp;
}

// 正确:引用传递,直接修改实参
void swap(int& p1, int& p2) {
    int tmp = p1;
    p1 = p2;
    p2 = tmp;
}

 

② 做函数形参:减少拷贝,提高效率

当参数是大型结构体 / 类时,用引用可以避免整个对象拷贝,提升性能:

struct A { /* 大型数据 */ };

// 传值:拷贝整个A对象
void func(struct A aa) {}

// 传引用:只传别名,不拷贝
void func(struct A& aa) {}
③ 做返回值类型:修改返回的对象

传值返回会生成临时变量,无法被修改;传引用返回可以直接修改原对象:

// 传值返回:返回临时变量,无法被赋值
int SLAt(SL& sl, int i) {
    assert(i < sl.size);
    return sl.a[i];
}

// 传引用返回:返回元素的引用,可以直接修改
int& SLAt(SL& sl, int i) {
    assert(i < sl.size);
    return sl.a[i];
}

int main() {
    SL s;
    SLAt(s, 0) = 10; // 传引用返回时合法,传值返回会报错
    return 0;
}
④ 做返回值类型:减少拷贝,提高效率

返回大型对象时,引用返回避免拷贝临时对象,提升性能。


3. 引用的安全问题:不能返回局部变量的引用

如果函数返回局部变量的引用,函数结束后局部变量的内存会被销毁,引用就会变成 “野引用”,导致未定义行为。

错误示例
int& func() {
    int ret = 0; // 局部变量
    return ret;  // 错误:返回局部变量的引用
}

int main() {
    int& x = func();
    cout << x << endl; // 内存已销毁,结果不可预测
    return 0;
}
原因图解
func() 栈帧:
┌─────────────┐
│ ret = 0     │  ← 函数结束后,这块内存被销毁
└─────────────┘

main() 栈帧:
┌─────────────┐
│ x = &ret    │  ← ret已销毁,x指向无效内存
└─────────────┘

四、指针引用的应用:链表头结点修改

当需要在函数中修改链表头指针时,用指针引用可以直接修改原指针,避免二级指针的复杂写法:

typedef struct ListNode {
    int val;
    struct ListNode* next;
} LTNode, *PNode;

// 用指针引用修改头结点
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);
    ListPushBack(plist, 2);
    return 0;
}

五、总结

特性

核心作用

缺省参数

简化函数调用,给参数提供默认值,注意声明和定义不能同时给缺省值

函数重载

同一作用域定义同名函数,通过形参区分,实现 “多态调用”

引用

变量的别名,可用于修改实参、减少拷贝、修改返回对象,但不能返回局部变量引用


更多推荐