C++函数重载与引用详解
一、函数重载:同名函数的 “差异化生存”
1. 概念定义
函数重载(Function Overloading)是 C++ 的重要特性,指在同一作用域内,允许存在多个同名函数,但要求这些函数的参数列表必须不同。
(参数个数、参数类型、参数顺序至少有一项不同)非常重要!!。
编译器会根据调用时传入的实参,自动匹配到对应的函数,实现 “一个函数名,多种功能” 的效果。
2. 典型案例分析
案例 1:参数个数不同的重载
#include <iostream>
using namespace std;// 无参数版本
void print() {cout << "无参数调用" << endl;
}// 1个int参数版本
void print(int a) {cout << "int参数:" << a << endl;
}// 2个int参数版本
void print(int a, int b) {cout << "两个int参数:" << a << " " << b << endl;
}int main() {print(); // 匹配无参数版本print(10); // 匹配1个int参数版本print(20, 30); // 匹配2个int参数版本return 0;
}
输出结果:
无参数调用
int参数:10
两个int参数:20 30
案例 2:参数类型不同的重载
#include <iostream>
using namespace std;// int类型参数
int add(int a, int b) {return a + b;
}// double类型参数
double add(double a, double b) {return a + b;
}int main() {cout << "int加法:" << add(3, 5) << endl; // 匹配int版本,输出8cout << "double加法:" << add(2.5, 4.5) << endl; // 匹配double版本,输出7.0return 0;
}
注意:仅返回值类型不同的函数,不能构成重载。例如int add(int a)和double add(int a),编译器无法通过调用语句(如add(10))区分,会报 “重定义” 错误。
案例 3:参数顺序不同的重载
#include <iostream>#include <string>using namespace std;// 先int后stringvoid showInfo(int age, string name) {cout << "年龄:" << age << ",姓名:" << name << endl;}// 先string后intvoid showInfo(string name, int age) {cout << "姓名:" << name << ",年龄:" << age << endl;}int main() {showInfo(20, "张三"); // 匹配先int后string版本showInfo("李四", 22); // 匹配先string后int版本return 0;}
输出结果:
年龄:20,姓名:张三姓名:李四,年龄:22
案例 4:多文件分离(声明与定义分离)
实际项目中,函数重载常涉及多文件拆分,需注意 “声明与定义的一致性”。
#ifndef FUNC_H
#define FUNC_H // 防止头文件重复包含// 声明3个重载函数
void calculate(int a);
void calculate(double a);
void calculate(int a, double b);#endif
#include "func.h"
#include <iostream>
using namespace std;void calculate(int a) {cout << "int参数计算:" << a * 2 << endl;
}void calculate(double a) {cout << "double参数计算:" << a * 3 << endl;
}void calculate(int a, double b) {cout << "int+double参数计算:" << a + b << endl;
}
#include "func.h"
int main() {calculate(5); // 匹配int版本,输出10calculate(3.5); // 匹配double版本,输出10.5calculate(2, 4.8); // 匹配int+double版本,输出6.8return 0;
}
编译运行:需将三个文件一起编译(如g++ main.cpp func.cpp -o test),确保声明的重载函数在定义文件中都有对应的实现,否则会报 “未定义引用” 错误。
3. 原理:为什么 C++ 支持,C 语言不支持?
核心原因在于编译器对函数名的 “修饰规则” 不同:
- C 语言:编译器会直接使用函数原名作为最终的 “符号名”(如函数add(int a, int b),符号名就是add)。若存在同名函数,符号名重复,链接阶段会报错,因此 C 语言不支持函数重载。
- C++ 语言:编译器会根据函数的参数列表对函数名进行 “修饰”(也称 “命名粉碎”,Name Mangling),生成唯一的符号名。例如:
- add(int a, int b)可能被修饰为_Z3addii(3表示函数名长度,ii表示两个 int 参数);
- add(double a, double b)可能被修饰为_Z3adddd。
不同重载函数的修饰后符号名不同,编译器和链接器能准确识别,因此 C++ 支持函数重载。
4.引用:变量的 “别名” 与权限控制
1. 概念定义
引用(Reference)是 C++ 引入的新特性,本质是变量的别名,它与原变量共享同一块内存空间,对引用的操作等同于对原变量的操作。语法格式为:类型& 引用名 = 原变量;。
注意:引用必须在定义时初始化,且初始化后不能再指向其他变量(与指针的核心区别)。
2. 基础操作:定义与使用
#include <iostream>using namespace std;int main() {int a = 10;int& ref_a = a; // 定义引用ref_a,作为a的别名cout << "a的值:" << a << endl; // 输出10cout << "ref_a的值:" << ref_a << endl; // 输出10(与a共享内存)ref_a = 20; // 操作引用,等同于修改acout << "修改后a的值:" << a << endl; // 输出20cout << "a的地址:" << &a << endl; // 输出a的地址(如0x7ffeefbff4ac)cout << "ref_a的地址:" << &ref_a << endl; // 输出相同地址(证明共享内存)return 0;}
3. 权限控制:放大、缩小与平移
引用的权限遵循 “不能放大原变量的权限” 原则,否则编译器会报错;权限缩小或平移是允许的。以下是典型案例:
案例 1:权限平移(同权限引用)
原变量是普通变量,引用也为普通引用,权限一致,合法。
int a = 10;int& ref_a = a; // 合法:普通变量→普通引用,权限平移
案例 2:权限缩小(原变量权限 > 引用权限)
原变量是普通变量(可读可写),引用为const引用(只读),权限缩小,合法。
int a = 10;const int& ref_a = a; // 合法:普通变量→const引用,权限缩小ref_a = 20; // 错误:const引用只读,不能修改a = 20; // 正确:原变量是普通变量,可修改(修改后ref_a的值也会变)
案例 3:权限放大(原变量权限 < 引用权限)
原变量是const变量(只读),引用为普通引用(可读可写),权限放大,非法。
const int a = 10; // a是const变量,只读int& ref_a = a; // 错误:const变量→普通引用,权限放大,编译器报错
案例 4:临时变量的引用(易混淆点)
临时变量(如表达式结果、字面量)的权限是 “只读”,只能用const引用接收,否则会触发权限放大错误。
// 错误案例:临时变量→普通引用(权限放大)int& ref1 = 10; // 错误:10是临时变量,只读,普通引用无法接收int& ref2 = 3 + 5; // 错误:3+5的结果是临时变量,只读// 正确案例:临时变量→const引用(权限缩小)const int& ref3 = 10; // 合法:const引用可接收临时变量const int& ref4 = 3 + 5; // 合法:临时变量值为8,ref4指向该临时变量
易错点总结
- 引用未初始化:定义引用时必须绑定原变量,否则报错。
错误:int& ref;(编译器提示 “引用必须初始化”)。
- 引用指向临时变量未加 const:如上述案例 4,临时变量只能用const引用接收,普通引用会触发权限放大错误。
- 混淆引用与指针:引用初始化后不能改指向,指针可以改指向;引用不需要解引用(*),指针需要。
错误:int a=10, b=20; int& ref=a; ref=&b;(试图让引用指向 b,编译器报错)。
- 数组引用的语法错误:数组引用需指定数组大小,语法格式为类型 (&引用名)[数组大小] = 原数组;。
错误:int arr[5] = {1,2,3,4,5}; int& ref_arr = arr;(未指定数组大小);
正确:int (&ref_arr)[5] = arr;。
总结
- 函数重载:C++ 通过 “参数列表不同” 实现同名函数的重载,核心原理是编译器对函数名的 “修饰规则”(根据参数列表生成唯一符号名),而 C 语言因无此修饰,不支持重载。
- 引用:本质是变量的别名,需初始化且不可改指向;权限控制遵循 “不能放大” 原则,临时变量需用const引用接收,避免权限相关错误。