一、函数定义
函数是一段可重复使用的代码块,用来完成特定任务。在 C++ 中,函数定义包含以下几个部分:
1.返回值类型:函数返回的数据类型,可以是内置类型、类类型、指针或引用,无返回则写 void。
2.函数名:标识函数的名称,遵循标识符命名规则。
3.参数列表:逗号分隔的形参声明,可为空(写 void 或留空)。
4.函数体:花括号内的语句,实现具体功能。
示例:计算两个整数之和的函数
cpp
#include <iostream>
#include <vector>
int add(int a, int b);
int main() {
int addNum = add(1, 2);
printf("add(1, 2)的值为:%d",addNum);
return 0;
}
int add(int a, int b) {
return a + b;
}
二、函数声明(函数原型)
函数声明告诉编译器函数的名称、返回值类型及参数类型,不包含函数体,以分号结束。
作用:允许在定义之前调用函数,或者跨文件使用函数。通常放在头文件中。
cpp
#include <iostream>
// 函数声明(原型)
int add(int, int); // 参数名可省略,保留类型即可
int main() {
std::cout << add(3, 4) << std::endl; // 在定义前调用,合法
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
定义与声明的区别:
1.声明仅提供接口信息,不分配执行代码;
2.定义包含函数体,决定函数的行为;
3.一个函数可有多次声明,但只能有一次定义(单一定义规则 ODR)。
三、参数传递方式
C++ 函数参数的传递机制主要包括 值传递、指针传递、引用传递。理解它们的区别是写出正确、高效代码的关键。
1. 值传递
形参获得实参的副本,在函数内对形参的修改不影响外部的实参。
cpp
#include <iostream>
#include <vector>
void SwapByValue(int x, int y);
int main() {
int a = 10;
int b = 20;
SwapByValue(a, b);
// a 仍为 10,b 仍为 20
printf("a的值为:%d\n", a);
printf("b的值为:%d", b);
return 0;
}
void SwapByValue(int x, int y)
{
int temp = x;
x = y;
y = temp; // 只交换了副本
}
特点:
1.实参到形参发生拷贝,适合小型对象(如 int、double)。
2.大型对象(如 std::vector)拷贝开销大,应避免值传递。
3.函数内部对形参的修改不影响原对象,天然安全。
2. 指针传递
将实参的地址作为值传给指针形参。函数内可通过解引用操作原对象。
cpp
#include <iostream>
#include <vector>
void SwapByPointer(int* x, int* y);
int main() {
int a = 10;
int b = 20;
SwapByPointer(&a, &b);
// a 为 20,b 为 10
printf("a的值为:%d\n", a);
printf("b的值为:%d", b);
return 0;
}
void SwapByPointer(int* x, int* y) {
int temp = *x;
*x = *y;
*y = temp; // 通过地址修改了实参
}
特点:
1.传递的是地址值,仅拷贝一个地址(通常 4/8 字节),避免大对象拷贝。
2.可修改指针所指对象,也可用 const int* 指向只读对象。
3.指针可为空(nullptr),调用者必须保证指针有效,或被调函数需检查空指针。
4.适合需要"可选"参数(允许不传递对象)的场景,或与 C 风格接口交互。
3. 引用传递
引用是对象的别名,传递引用不会产生拷贝,直接操作原对象。
cpp
#include <iostream>
#include <vector>
void SwapByReference(int& x, int& y);
int main() {
int a = 10;
int b = 20;
// 无需取地址
SwapByReference(a, b);
// a 为 20,b 为 10
printf("a的值为:%d\n", a);
printf("b的值为:%d", b);
return 0;
}
void SwapByReference(int& x, int& y) {
int temp = x;
x = y;
y = temp; // 直接修改实参
}
特点:
1.语法简洁,像值传递一样使用,没有解引用开销。
2.引用必须初始化,不能为空,通常比指针更安全。
3.对引用的操作即是对原对象的操作。
4.是传递大型对象的首选方式(尤其配合 const)。
三种传递方式对比
| 传递方式 | 是否拷贝原对象 | 能否修改实参 | 是否可能为空 | 典型用途 |
|---|---|---|---|---|
| 值传递 | 拷贝整个对象 | 否 | 不可能 | 小型不可变参数 |
| 指针传递 | 拷贝地址(4/8字节) | 是(非 const 时) | 是(可传 nullptr) | C 接口、可选参数、需要改变指向 |
| 引用传递 | 不拷贝 | 是(非 const 时) | 不可能 | 大型对象、输出参数、运算符重载 |
四、函数重载
函数重载允许在同一作用域内定义多个同名函数,只要它们的参数列表不同。
参数列表不同的含义:
1.参数个数不同
2.参数类型不同
3.参数顺序不同(针对不同类型)
返回值类型不参与重载解析,仅返回类型不同无法构成重载。
示例:不同类型的打印函数
cpp
#include <iostream>
#include <vector>
void print(int i);
void print(double d);
void print(const std::string& s);
int main() {
print(10); // 调用 print(int)
print(3.14); // 调用 print(double)
print("hello"); // 调用 print(const std::string&),"hello" 可转换为 string
}
void print(int i) {
std::cout << "整数: " << i << std::endl;
}
void print(double d) {
std::cout << "双精度: " << d << std::endl;
}
void print(const std::string& s) {
std::cout << "字符串: " << s << std::endl;
}
重载决议:编译器根据实参类型寻找最佳匹配,规则大致为:
1.精确匹配(实参与形参类型完全相同)。
2.提升匹配(如 char → int、float → double)。
3。标准转换匹配(如 int → double、派生类指针→基类指针)。
4.用户定义转换(如转换构造函数、类型转换运算符)。
注意事项与常见陷阱:
值传递与引用传递重载可能产生歧义:
cpp
#include <iostream>
#include <vector>
void f(int x);
void f(int& x);
int main() {
int a = 1;
f(a);
f(a);
return 0;
}
void f(int x) {
x = 10;
printf("f(int x):%d", x);
}
void f(int& x) {
x = 10;
printf("f(int& x):%d", x);
}
编译时报错:有多个 重载函数 "f" 实例与参数列表匹配
仅靠 const 区分引用参数是合法的,且常用来区分读写版本:
cpp
#include <iostream>
#include <vector>
void g(const int& x); // 接受 const 对象、右值
void g(int& x); // 接受非 const 左值
int main() {
int a = 1;
const int b = 12;
g(a);
std::cout<< std::endl;
g(b);
return 0;
}
void g(const int& x) {
printf("g(int x):%d", x);
}
void g(int& x) {
printf("g(int& x):%d", x);
}
带有默认参数时,避免产生不同重载间的调用模糊,例如 void h(int a, double b = 0.0) 和 void h(int a) 同时存在,调用 h(5) 将报错。
csharp
#include <iostream>
#include <vector>
// 带有默认参数的版本
void h(int a, double b = 0.0) {
std::cout << "h(int, double) called: a=" << a << ", b=" << b << std::endl;
}
// 单参数重载版本
void h(int a) {
std::cout << "h(int) called: a=" << a << std::endl;
}
int main() {
int a = 5;
h(a);
return 0;
}
编译时报错:E0308 有多个 重载函数 "h" 实例与参数列表匹配。
顶层 const 不影响重载:void f(int) 和 void f(const int) 视为同一签名(因为值传递时顶层 const 被忽略)。但 const 引用或指针则属于不同签名:void f(int*) 和 void f(const int*) 是不同的。
演示"底层 const 影响重载",应该删除值传递版本,只保留指针和引用重载:
cpp
#include <iostream>
// 指针重载:底层 const 决定不同签名
void f(int* p) {
std::cout << "f(int*) called, *p = " << (p ? *p : 0) << std::endl;
}
void f(const int* p) {
std::cout << "f(const int*) called, *p = " << (p ? *p : 0) << std::endl;
}
// 引用重载:底层 const 决定不同签名
void f(int& r) {
std::cout << "f(int&) called, r = " << r << std::endl;
}
void f(const int& r) {
std::cout << "f(const int&) called, r = " << r << std::endl;
}
int main() {
// ---------- 指针重载调用演示 ----------
int a = 10;
const int b = 20;
f(&a); // 传入 int*,匹配 f(int*)
f(&b); // 传入 const int*,匹配 f(const int*)
// 若需要传递空指针,必须显式转换以消除歧义
f(static_cast<int*>(nullptr)); // 调 f(int*)
f(static_cast<const int*>(nullptr)); // 调 f(const int*)
// ---------- 引用重载调用演示 ----------
int x = 100;
const int y = 200;
f(x); // x 是 int 左值,匹配 f(int&)
f(y); // y 是 const int 左值,匹配 f(const int&)
f(300); // 300 是右值,只能绑定到 const 引用,匹配 f(const int&)
return 0;
}