文章目录
-
- [第8章 函数基础](#第8章 函数基础)
-
- [8.1 为什么需要函数?](#8.1 为什么需要函数?)
- [8.2 函数的基本语法](#8.2 函数的基本语法)
-
- [8.2.1 函数的定义与调用](#8.2.1 函数的定义与调用)
- [8.2.2 函数声明与定义分离](#8.2.2 函数声明与定义分离)
- [8.3 函数的参数传递](#8.3 函数的参数传递)
-
- [8.3.1 值传递(Pass by Value)](#8.3.1 值传递(Pass by Value))
- [8.3.2 引用传递(Pass by Reference)](#8.3.2 引用传递(Pass by Reference))
- [8.3.3 指针传递(Pass by Pointer)](#8.3.3 指针传递(Pass by Pointer))
- [8.3.4 参数传递方式对比](#8.3.4 参数传递方式对比)
- [8.4 函数的返回值](#8.4 函数的返回值)
-
- [8.4.1 基本返回值](#8.4.1 基本返回值)
- [8.4.2 返回引用与指针](#8.4.2 返回引用与指针)
- [8.4.3 void函数与无返回值](#8.4.3 void函数与无返回值)
- [8.5 默认参数](#8.5 默认参数)
-
- [8.5.1 基本默认参数](#8.5.1 基本默认参数)
- [8.5.2 默认参数的注意事项](#8.5.2 默认参数的注意事项)
- [8.6 函数重载](#8.6 函数重载)
-
- [8.6.1 基本函数重载](#8.6.1 基本函数重载)
- [8.6.2 函数重载的规则与限制](#8.6.2 函数重载的规则与限制)
- [8.7 内联函数](#8.7 内联函数)
-
- [8.7.1 内联函数基础](#8.7.1 内联函数基础)
- [8.7.2 内联与宏的对比](#8.7.2 内联与宏的对比)
- [8.8 实践练习](#8.8 实践练习)
- [8.9 常见错误与调试](#8.9 常见错误与调试)
-
- [8.9.1 函数常见错误](#8.9.1 函数常见错误)
第8章 函数基础
8.1 为什么需要函数?
函数的必要性:
- 代码复用:避免重复编写相同代码
- 模块化:将复杂问题分解为小问题
- 可维护性:修改只需在一个地方进行
- 可读性:通过有意义的函数名提高代码可读性
- 团队协作:不同开发者可以并行开发不同函数
函数的核心概念:
- 函数定义:实现函数功能的代码块
- 函数声明(原型):告诉编译器函数的存在和接口
- 函数调用:使用函数执行特定任务
- 参数:传递给函数的数据
- 返回值:函数执行后返回的结果
8.2 函数的基本语法
8.2.1 函数的定义与调用
cpp
#include <iostream>
using namespace std;
// 函数定义
// 返回类型 函数名(参数列表) { 函数体 }
int add(int a, int b) {
int result = a + b;
return result; // 返回结果
}
// 无参数、无返回值的函数
void greet() {
cout << "Hello, welcome to C++ functions!" << endl;
}
// 多个参数的函数
double calculateAverage(double num1, double num2, double num3) {
return (num1 + num2 + num3) / 3.0;
}
int main() {
cout << "=== 函数的基本使用 ===" << endl;
// 调用函数
greet(); // 调用无参数函数
// 调用有返回值的函数
int sum = add(5, 3);
cout << "5 + 3 = " << sum << endl;
// 直接在表达式中使用函数调用
cout << "10 + 20 = " << add(10, 20) << endl;
// 调用多个参数的函数
double average = calculateAverage(85.5, 90.0, 88.5);
cout << "平均分: " << average << endl;
// 嵌套函数调用
int result = add(add(1, 2), add(3, 4));
cout << "(1+2)+(3+4) = " << result << endl;
return 0;
}
8.2.2 函数声明与定义分离
cpp
#include <iostream>
using namespace std;
// 函数声明(原型)
// 告诉编译器函数的存在,但不提供实现
int multiply(int a, int b);
void printMessage(string message);
double getCircleArea(double radius);
int findMaximum(int arr[], int size);
int main() {
cout << "=== 函数声明与定义分离 ===" << endl;
// 函数调用 - 编译器只需要知道声明
int product = multiply(7, 8);
cout << "7 × 8 = " << product << endl;
printMessage("Hello from function!");
double radius = 5.0;
double area = getCircleArea(radius);
cout << "半径为 " << radius << " 的圆面积: " << area << endl;
int numbers[] = {3, 7, 2, 9, 4, 1};
int maxNum = findMaximum(numbers, 6);
cout << "数组中的最大值: " << maxNum << endl;
return 0;
}
// 函数定义(实现)
int multiply(int a, int b) {
return a * b;
}
void printMessage(string message) {
cout << "消息: " << message << endl;
}
double getCircleArea(double radius) {
const double PI = 3.141592653589793;
return PI * radius * radius;
}
int findMaximum(int arr[], int size) {
if (size <= 0) {
return -1; // 错误代码
}
int max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
8.3 函数的参数传递
8.3.1 值传递(Pass by Value)
cpp
#include <iostream>
using namespace std;
// 值传递:创建参数的副本,函数内修改不影响原始变量
void swapByValue(int a, int b) {
cout << "函数内交换前: a=" << a << ", b=" << b << endl;
int temp = a;
a = b;
b = temp;
cout << "函数内交换后: a=" << a << ", b=" << b << endl;
}
// 值传递示例:修改不会影响调用者
void increment(int num) {
num++; // 只修改副本
cout << "函数内: num=" << num << endl;
}
// 值传递的典型应用:计算不改变原始数据
double calculatePower(double base, int exponent) {
double result = 1.0;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
int main() {
cout << "=== 值传递 ===" << endl;
// 示例1:尝试交换变量
int x = 5, y = 10;
cout << "交换前: x=" << x << ", y=" << y << endl;
swapByValue(x, y);
cout << "交换后: x=" << x << ", y=" << y << " (未改变)" << endl;
// 示例2:修改参数
int value = 10;
cout << "\n调用前: value=" << value << endl;
increment(value);
cout << "调用后: value=" << value << " (未改变)" << endl;
// 示例3:计算函数
double base = 2.0;
int exp = 3;
double power = calculatePower(base, exp);
cout << "\n" << base << " 的 " << exp << " 次方 = " << power << endl;
return 0;
}
8.3.2 引用传递(Pass by Reference)
cpp
#include <iostream>
using namespace std;
// 引用传递:使用参数的引用,函数内修改影响原始变量
void swapByReference(int &a, int &b) {
cout << "函数内交换前: a=" << a << ", b=" << b << endl;
int temp = a;
a = b;
b = temp;
cout << "函数内交换后: a=" << a << ", b=" << b << endl;
}
// 引用传递:真正修改原始变量
void incrementByReference(int &num) {
num++;
cout << "函数内: num=" << num << endl;
}
// 返回多个值(通过引用参数)
void calculateMinMax(const int arr[], int size, int &min, int &max) {
if (size <= 0) {
min = max = -1;
return;
}
min = max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < min) {
min = arr[i];
}
if (arr[i] > max) {
max = arr[i];
}
}
}
// 避免拷贝大型对象
struct LargeData {
int data[1000];
// ... 其他大型数据
};
void processLargeData(const LargeData &data) {
// 使用const引用,避免拷贝,且不能修改数据
cout << "处理大型数据,使用引用避免拷贝" << endl;
}
int main() {
cout << "=== 引用传递 ===" << endl;
// 示例1:交换变量(实际有效)
int x = 5, y = 10;
cout << "交换前: x=" << x << ", y=" << y << endl;
swapByReference(x, y);
cout << "交换后: x=" << x << ", y=" << y << " (已改变)" << endl;
// 示例2:修改原始变量
int value = 10;
cout << "\n调用前: value=" << value << endl;
incrementByReference(value);
cout << "调用后: value=" << value << " (已改变)" << endl;
// 示例3:返回多个值
int numbers[] = {3, 7, 2, 9, 4, 1, 8};
int minValue, maxValue;
calculateMinMax(numbers, 7, minValue, maxValue);
cout << "\n数组中的最小值: " << minValue << endl;
cout << "数组中的最大值: " << maxValue << endl;
// 示例4:避免大型对象拷贝
LargeData bigData;
// 初始化数据...
processLargeData(bigData); // 传递引用,避免拷贝
return 0;
}
8.3.3 指针传递(Pass by Pointer)
cpp
#include <iostream>
using namespace std;
// 指针传递:使用指针作为参数
void swapByPointer(int *a, int *b) {
if (a == nullptr || b == nullptr) {
cout << "错误:空指针!" << endl;
return;
}
cout << "函数内交换前: *a=" << *a << ", *b=" << *b << endl;
int temp = *a;
*a = *b;
*b = temp;
cout << "函数内交换后: *a=" << *a << ", *b=" << *b << endl;
}
// 指针传递:可以修改指针指向的内容,也可以修改指针本身
void allocateArray(int **arrPtr, int size) {
*arrPtr = new int[size]; // 分配动态数组
for (int i = 0; i < size; i++) {
(*arrPtr)[i] = i * 10; // 初始化数组
}
}
// 指针传递的典型应用:处理数组
void printArray(const int *arr, int size) {
if (arr == nullptr) {
cout << "空数组" << endl;
return;
}
cout << "数组元素: ";
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// 修改指针本身
void reassignPointer(int **ptr) {
static int newValue = 100;
*ptr = &newValue; // 修改指针指向的内容
}
int main() {
cout << "=== 指针传递 ===" << endl;
// 示例1:交换变量
int x = 5, y = 10;
cout << "交换前: x=" << x << ", y=" << y << endl;
swapByPointer(&x, &y); // 传递地址
cout << "交换后: x=" << x << ", y=" << y << endl;
// 示例2:动态分配数组
int *dynamicArray = nullptr;
int arraySize = 5;
allocateArray(&dynamicArray, arraySize);
printArray(dynamicArray, arraySize);
// 清理动态内存
delete[] dynamicArray;
// 示例3:修改指针本身
int value = 50;
int *ptr = &value;
cout << "\n修改指针前:" << endl;
cout << "指针指向的值: " << *ptr << endl;
cout << "指针地址: " << ptr << endl;
reassignPointer(&ptr);
cout << "\n修改指针后:" << endl;
cout << "指针指向的值: " << *ptr << endl;
cout << "指针地址: " << ptr << endl;
return 0;
}
8.3.4 参数传递方式对比
cpp
#include <iostream>
using namespace std;
// 三种参数传递方式的对比
class Data {
public:
int value;
Data(int v) : value(v) {
cout << "构造函数调用: value=" << value << endl;
}
Data(const Data& other) : value(other.value) {
cout << "拷贝构造函数调用: value=" << value << endl;
}
~Data() {
cout << "析构函数调用: value=" << value << endl;
}
};
// 1. 值传递:调用拷贝构造函数,创建副本
void passByValue(Data data) {
cout << "值传递函数内: data.value=" << data.value << endl;
data.value = 999; // 只修改副本
}
// 2. 引用传递:不调用拷贝构造函数,直接使用原始对象
void passByReference(Data &data) {
cout << "引用传递函数内: data.value=" << data.value << endl;
data.value = 999; // 修改原始对象
}
// 3. 常量引用传递:不调用拷贝构造函数,不能修改对象
void passByConstReference(const Data &data) {
cout << "常量引用传递函数内: data.value=" << data.value << endl;
// data.value = 999; // 错误:不能修改const引用
}
// 4. 指针传递
void passByPointer(Data *data) {
if (data != nullptr) {
cout << "指针传递函数内: data->value=" << data->value << endl;
data->value = 999; // 修改原始对象
}
}
int main() {
cout << "=== 参数传递方式对比 ===" << endl;
cout << "\n1. 值传递演示:" << endl;
{
Data d1(100);
cout << "调用前: d1.value=" << d1.value << endl;
passByValue(d1); // 调用拷贝构造函数
cout << "调用后: d1.value=" << d1.value << " (未改变)" << endl;
}
cout << "\n2. 引用传递演示:" << endl;
{
Data d2(200);
cout << "调用前: d2.value=" << d2.value << endl;
passByReference(d2); // 不调用拷贝构造函数
cout << "调用后: d2.value=" << d2.value << " (已改变)" << endl;
}
cout << "\n3. 常量引用传递演示:" << endl;
{
Data d3(300);
cout << "调用前: d3.value=" << d3.value << endl;
passByConstReference(d3); // 不调用拷贝构造函数
cout << "调用后: d3.value=" << d3.value << " (未改变)" << endl;
}
cout << "\n4. 指针传递演示:" << endl;
{
Data d4(400);
cout << "调用前: d4.value=" << d4.value << endl;
passByPointer(&d4);
cout << "调用后: d4.value=" << d4.value << " (已改变)" << endl;
}
cout << "\n=== 总结 ===" << endl;
cout << "值传递: 安全,但可能低效(调用拷贝构造函数)" << endl;
cout << "引用传递: 高效,可以修改原始数据" << endl;
cout << "常量引用传递: 高效,安全(不能修改)" << endl;
cout << "指针传递: 灵活,可以修改指针本身,需要检查nullptr" << endl;
return 0;
}
8.4 函数的返回值
8.4.1 基本返回值
cpp
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
// 返回基本类型
int getSquare(int num) {
return num * num; // 返回int
}
// 返回浮点数
double getDistance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return sqrt(dx * dx + dy * dy);
}
// 返回布尔值
bool isPrime(int num) {
if (num <= 1) return false;
if (num == 2) return true;
if (num % 2 == 0) return false;
for (int i = 3; i * i <= num; i += 2) {
if (num % i == 0) {
return false;
}
}
return true;
}
// 返回字符
char getGrade(double score) {
if (score >= 90) return 'A';
else if (score >= 80) return 'B';
else if (score >= 70) return 'C';
else if (score >= 60) return 'D';
else return 'F';
}
// 返回字符串
string getDayOfWeek(int day) {
switch (day) {
case 1: return "Monday";
case 2: return "Tuesday";
case 3: return "Wednesday";
case 4: return "Thursday";
case 5: return "Friday";
case 6: return "Saturday";
case 7: return "Sunday";
default: return "Invalid day";
}
}
// 返回数组(通过指针)
int* createSequence(int start, int end) {
if (start > end) {
return nullptr;
}
int size = end - start + 1;
int* sequence = new int[size];
for (int i = 0; i < size; i++) {
sequence[i] = start + i;
}
return sequence; // 返回动态数组指针
}
int main() {
cout << "=== 函数的返回值 ===" << endl;
// 使用不同类型的返回值
int square = getSquare(5);
cout << "5的平方: " << square << endl;
double distance = getDistance(0, 0, 3, 4);
cout << "点(0,0)到(3,4)的距离: " << distance << endl;
int num = 17;
cout << num << "是质数吗? " << (isPrime(num) ? "是" : "否") << endl;
double score = 85.5;
cout << "分数" << score << "的等级: " << getGrade(score) << endl;
cout << "星期" << 3 << "是: " << getDayOfWeek(3) << endl;
// 使用返回的动态数组
int* sequence = createSequence(1, 10);
if (sequence != nullptr) {
cout << "序列: ";
for (int i = 0; i < 10; i++) {
cout << sequence[i] << " ";
}
cout << endl;
// 记得释放动态内存
delete[] sequence;
}
return 0;
}
8.4.2 返回引用与指针
cpp
#include <iostream>
#include <string>
using namespace std;
// 1. 返回引用(必须确保引用的对象在函数返回后仍然存在)
int& getLarger(int &a, int &b) {
return (a > b) ? a : b; // 返回较大值的引用
}
// 危险:返回局部变量的引用(未定义行为)
int& badFunction() {
int localVar = 100; // 局部变量,函数结束后被销毁
return localVar; // 错误:返回局部变量的引用
}
// 正确:返回静态局部变量的引用
int& getCounter() {
static int counter = 0; // 静态局部变量,生命周期是整个程序
counter++;
return counter;
}
// 2. 返回指针
int* findValue(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
return &arr[i]; // 返回找到的元素的地址
}
}
return nullptr; // 未找到
}
// 返回动态分配的内存
char* duplicateString(const char* str) {
if (str == nullptr) return nullptr;
int length = 0;
while (str[length] != '\0') {
length++;
}
char* copy = new char[length + 1]; // +1 for null terminator
for (int i = 0; i <= length; i++) {
copy[i] = str[i];
}
return copy; // 调用者负责释放内存
}
// 3. 返回常量引用(只读访问)
const string& getDefaultName() {
static const string defaultName = "Unknown"; // 静态常量
return defaultName;
}
int main() {
cout << "=== 返回引用与指针 ===" << endl;
// 示例1:返回引用
int x = 5, y = 10;
cout << "原始值: x=" << x << ", y=" << y << endl;
int& larger = getLarger(x, y);
cout << "较大的值: " << larger << endl;
// 通过引用修改原始值
larger = 100;
cout << "修改后: x=" << x << ", y=" << y << endl;
// 示例2:返回静态局部变量的引用
cout << "\n计数器: ";
for (int i = 0; i < 5; i++) {
cout << getCounter() << " ";
}
cout << endl;
// 示例3:返回指针查找元素
int numbers[] = {10, 20, 30, 40, 50};
int* found = findValue(numbers, 5, 30);
if (found != nullptr) {
cout << "找到值 " << *found << " 在位置 " << (found - numbers) << endl;
*found = 99; // 通过指针修改值
cout << "修改后: " << numbers[2] << endl;
}
// 示例4:返回动态字符串
const char* original = "Hello, C++!";
char* copy = duplicateString(original);
if (copy != nullptr) {
cout << "\n原字符串: " << original << endl;
cout << "复制字符串: " << copy << endl;
delete[] copy; // 释放内存
}
// 示例5:返回常量引用
const string& name = getDefaultName();
cout << "\n默认名称: " << name << endl;
// name = "Changed"; // 错误:不能修改常量引用
return 0;
}
8.4.3 void函数与无返回值
cpp
#include <iostream>
#include <iomanip>
using namespace std;
// void函数:不返回任何值
void printHeader() {
cout << "===================================" << endl;
cout << " 学生成绩管理系统 " << endl;
cout << "===================================" << endl;
}
// void函数带参数
void printStudentInfo(string name, int age, double score) {
cout << fixed << setprecision(2);
cout << "姓名: " << name << endl;
cout << "年龄: " << age << "岁" << endl;
cout << "成绩: " << score << "分" << endl;
cout << "等级: " << (score >= 60 ? "及格" : "不及格") << endl;
}
// void函数可以通过引用参数"返回"值
void calculateStatistics(const double scores[], int size,
double &average, double &min, double &max) {
if (size <= 0) {
average = min = max = 0.0;
return;
}
double sum = 0.0;
min = max = scores[0];
for (int i = 0; i < size; i++) {
sum += scores[i];
if (scores[i] < min) min = scores[i];
if (scores[i] > max) max = scores[i];
}
average = sum / size;
}
// 纯副作用函数:只产生副作用,不返回计算结果
void logMessage(const string& message, bool isError = false) {
if (isError) {
cerr << "[ERROR] " << message << endl;
} else {
cout << "[INFO] " << message << endl;
}
}
// void函数可以提前返回
void processNumber(int num) {
if (num < 0) {
cout << "错误:负数无效" << endl;
return; // 提前返回
}
if (num == 0) {
cout << "零没有倒数" << endl;
return; // 提前返回
}
cout << num << " 的倒数是: " << 1.0 / num << endl;
}
// 递归void函数
void countDown(int n) {
if (n <= 0) {
cout << "发射!" << endl;
return;
}
cout << n << "..." << endl;
countDown(n - 1); // 递归调用
}
int main() {
cout << "=== void函数与无返回值 ===" << endl;
// 调用void函数
printHeader();
cout << endl;
printStudentInfo("张三", 20, 85.5);
cout << endl;
printStudentInfo("李四", 19, 58.0);
// 通过引用参数获取计算结果
double testScores[] = {85.5, 92.0, 78.5, 88.0, 95.5};
double avg, minScore, maxScore;
calculateStatistics(testScores, 5, avg, minScore, maxScore);
cout << fixed << setprecision(2);
cout << "\n成绩统计:" << endl;
cout << "平均分: " << avg << endl;
cout << "最低分: " << minScore << endl;
cout << "最高分: " << maxScore << endl;
// 日志函数
logMessage("程序启动成功");
logMessage("文件读取失败", true);
// 提前返回的void函数
cout << "\n处理数字:" << endl;
processNumber(5);
processNumber(0);
processNumber(-3);
// 递归void函数
cout << "\n倒计时:" << endl;
countDown(5);
return 0;
}
8.5 默认参数
8.5.1 基本默认参数
cpp
#include <iostream>
#include <string>
using namespace std;
// 默认参数:在函数声明中指定参数的默认值
void printMessage(string message = "Hello, World!") {
cout << message << endl;
}
// 多个默认参数
void createWindow(string title = "Untitled",
int width = 800,
int height = 600,
bool fullscreen = false) {
cout << "创建窗口: " << endl;
cout << " 标题: " << title << endl;
cout << " 宽度: " << width << endl;
cout << " 高度: " << height << endl;
cout << " 全屏: " << (fullscreen ? "是" : "否") << endl;
cout << endl;
}
// 混合默认参数和非默认参数
// 注意:默认参数必须从右向左连续
double calculatePrice(double basePrice,
double taxRate = 0.08,
double discount = 0.0) {
double priceAfterDiscount = basePrice * (1 - discount);
return priceAfterDiscount * (1 + taxRate);
}
// 错误示例:默认参数不在最右边
/*
void badFunction(int a = 1, int b) { // 错误!
// ...
}
*/
// 正确:默认参数从右向左
void goodFunction(int a, int b = 2, int c = 3) {
cout << "a=" << a << ", b=" << b << ", c=" << c << endl;
}
int main() {
cout << "=== 默认参数 ===" << endl;
// 使用默认参数
printMessage(); // 使用默认值
printMessage("你好!"); // 提供自定义值
// 使用多个默认参数的不同组合
createWindow(); // 全部使用默认值
createWindow("我的程序"); // 只提供标题
createWindow("游戏窗口", 1024, 768); // 提供标题、宽度、高度
createWindow("全屏应用", 1920, 1080, true); // 提供所有参数
// 计算价格
cout << "价格计算:" << endl;
double basePrice = 100.0;
cout << "基础价格: " << basePrice << endl;
cout << "含税(8%): " << calculatePrice(basePrice) << endl;
cout << "含税+9折: " << calculatePrice(basePrice, 0.08, 0.1) << endl;
cout << "免税+8折: " << calculatePrice(basePrice, 0.0, 0.2) << endl;
// 跳过中间参数(C++不支持直接跳过,需要重载函数)
// calculatePrice(100.0, 0.1); // 错误:不能跳过taxRate直接指定discount
// 默认参数从右向左
cout << "\n默认参数顺序:" << endl;
goodFunction(1); // a=1, b=2, c=3
goodFunction(1, 4); // a=1, b=4, c=3
goodFunction(1, 4, 5); // a=1, b=4, c=5
return 0;
}
8.5.2 默认参数的注意事项
cpp
#include <iostream>
using namespace std;
// 问题1:默认参数在声明中指定,定义中不要重复指定
// 函数声明(通常在头文件中)
void display(int value, bool showHex = false);
// 函数定义(在源文件中)
void display(int value, bool showHex /* = false 不要在这里写 */) {
if (showHex) {
cout << "十六进制: 0x" << hex << value << endl;
} else {
cout << "十进制: " << dec << value << endl;
}
}
// 问题2:默认参数与函数重载的冲突
// 版本1:默认参数
void process(int a, int b = 10) {
cout << "process(int, int): a=" << a << ", b=" << b << endl;
}
// 版本2:重载函数
void process(int a) {
cout << "process(int): a=" << a << endl;
}
// 这会产生歧义:process(5) 调用哪个?
// 问题3:默认参数的值在调用时确定
int getDefaultValue() {
static int counter = 0;
return ++counter;
}
// 默认参数是表达式
void testFunction(int a = getDefaultValue()) {
cout << "a = " << a << endl;
}
// 问题4:默认参数与指针/引用
void setupConnection(string address = "localhost",
int port = 8080,
int* status = nullptr) {
cout << "连接到 " << address << ":" << port << endl;
if (status != nullptr) {
*status = 0; // 成功
}
}
// 问题5:默认参数与const
void printData(const string& data = "") {
cout << "数据: " << data << endl;
}
int main() {
cout << "=== 默认参数的注意事项 ===" << endl;
// 示例1:声明和定义分离
display(255);
display(255, true);
// 示例2:函数重载冲突
process(5); // 调用哪个?有歧义!
process(5, 20); // 明确调用第一个版本
// 示例3:默认参数表达式
cout << "\n默认参数表达式:" << endl;
testFunction(); // a = 1
testFunction(); // a = 2(每次调用表达式都会重新求值)
testFunction(100); // a = 100(使用提供的值)
// 示例4:指针默认参数
cout << "\n指针默认参数:" << endl;
int statusCode;
setupConnection(); // 使用所有默认值
setupConnection("example.com", 443, &statusCode);
cout << "状态码: " << statusCode << endl;
// 示例5:const引用默认参数
printData();
printData("Hello");
// 最佳实践建议:
cout << "\n=== 最佳实践 ===" << endl;
cout << "1. 在函数声明中指定默认参数,不要在定义中重复" << endl;
cout << "2. 避免默认参数导致函数重载歧义" << endl;
cout << "3. 默认参数可以是常量、全局变量或静态变量的表达式" << endl;
cout << "4. 对于复杂默认值,考虑使用函数重载代替" << endl;
cout << "5. 指针默认参数通常使用nullptr" << endl;
return 0;
}
8.6 函数重载
8.6.1 基本函数重载
cpp
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
// 函数重载:同名函数,不同参数列表
// 版本1:两个整数相加
int add(int a, int b) {
cout << "调用 int add(int, int)" << endl;
return a + b;
}
// 版本2:两个浮点数相加
double add(double a, double b) {
cout << "调用 double add(double, double)" << endl;
return a + b;
}
// 版本3:三个整数相加
int add(int a, int b, int c) {
cout << "调用 int add(int, int, int)" << endl;
return a + b + c;
}
// 版本4:两个字符串相加(连接)
string add(const string& a, const string& b) {
cout << "调用 string add(const string&, const string&)" << endl;
return a + b;
}
// 版本5:整数数组求和
int add(const int arr[], int size) {
cout << "调用 int add(const int[], int)" << endl;
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
// 计算面积的不同重载
double area(double radius) { // 圆面积
return 3.14159 * radius * radius;
}
double area(double length, double width) { // 矩形面积
return length * width;
}
double area(double base, double height, bool isTriangle) { // 三角形面积
if (isTriangle) {
return 0.5 * base * height;
}
return base * height; // 实际上是平行四边形
}
// 显示不同类型的值
void display(int value) {
cout << "整数: " << value << endl;
}
void display(double value) {
cout << "浮点数: " << value << endl;
}
void display(const char* value) {
cout << "字符串: " << value << endl;
}
void display(bool value) {
cout << "布尔值: " << (value ? "true" : "false") << endl;
}
int main() {
cout << "=== 函数重载 ===" << endl;
// 自动选择正确的重载版本
cout << "\n1. 加法函数重载:" << endl;
cout << "整数相加: " << add(5, 3) << endl;
cout << "浮点数相加: " << add(5.5, 3.3) << endl;
cout << "三个整数相加: " << add(1, 2, 3) << endl;
cout << "字符串连接: " << add("Hello, ", "World!") << endl;
int numbers[] = {1, 2, 3, 4, 5};
cout << "数组求和: " << add(numbers, 5) << endl;
// 类型转换和重载解析
cout << "\n2. 类型转换示例:" << endl;
cout << "add(5, 3.14) = " << add(5, 3.14) << endl; // 调用double版本
// 发生隐式类型转换:int转换为double
// 面积计算重载
cout << "\n3. 面积计算重载:" << endl;
cout << "圆面积 (半径=5): " << area(5.0) << endl;
cout << "矩形面积 (长=4, 宽=6): " << area(4.0, 6.0) << endl;
cout << "三角形面积 (底=3, 高=4): " << area(3.0, 4.0, true) << endl;
// 显示函数重载
cout << "\n4. 显示不同类型:" << endl;
display(42);
display(3.14159);
display("C++ Programming");
display(true);
return 0;
}
8.6.2 函数重载的规则与限制
cpp
#include <iostream>
using namespace std;
// 规则1:重载基于参数列表(类型、个数、顺序),而不是返回类型
int process(int a) {
cout << "process(int)" << endl;
return a * 2;
}
// 错误:仅返回类型不同不能重载
/*
double process(int a) { // 编译错误
return a * 2.0;
}
*/
// 正确:参数类型不同可以重载
double process(double a) {
cout << "process(double)" << endl;
return a * 2.0;
}
// 正确:参数个数不同可以重载
int process(int a, int b) {
cout << "process(int, int)" << endl;
return a + b;
}
// 正确:参数顺序不同可以重载
void configure(int width, double ratio) {
cout << "configure(int, double)" << endl;
}
void configure(double ratio, int width) {
cout << "configure(double, int)" << endl;
}
// 规则2:const可以作为重载依据
class Data {
public:
void print() {
cout << "非const版本" << endl;
}
void print() const { // const成员函数
cout << "const版本" << endl;
}
};
// 规则3:引用和const引用可以作为重载依据
void handleValue(int& x) {
cout << "可修改引用版本" << endl;
x++;
}
void handleValue(const int& x) {
cout << "只读引用版本,值=" << x << endl;
}
// 规则4:指针类型也可以重载
void analyze(int* ptr) {
cout << "整数指针版本" << endl;
}
void analyze(double* ptr) {
cout << "浮点数指针版本" << endl;
}
// 规则5:默认参数可能引起重载歧义
void compute(int a, int b = 0) {
cout << "compute(int, int) with default" << endl;
}
// 这个重载会导致歧义:compute(5) 调用哪个?
void compute(int a) {
cout << "compute(int)" << endl;
}
// 测试重载解析
void test(int a) {
cout << "test(int)" << endl;
}
void test(double a) {
cout << "test(double)" << endl;
}
void test(int a, int b) {
cout << "test(int, int)" << endl;
}
int main() {
cout << "=== 函数重载的规则与限制 ===" << endl;
// 测试不同参数类型
cout << "\n1. 不同参数类型:" << endl;
process(10); // 调用int版本
process(10.0); // 调用double版本
process(10, 20); // 调用两个int版本
// 测试参数顺序
cout << "\n2. 不同参数顺序:" << endl;
configure(100, 1.5); // int, double
configure(1.5, 100); // double, int
// 测试const重载
cout << "\n3. const重载:" << endl;
Data d1;
const Data d2;
d1.print(); // 调用非const版本
d2.print(); // 调用const版本
// 测试引用重载
cout << "\n4. 引用重载:" << endl;
int value = 10;
handleValue(value); // 调用可修改版本
handleValue(20); // 调用只读版本(字面量)
handleValue(value); // 仍然调用可修改版本
// 测试指针重载
cout << "\n5. 指针重载:" << endl;
int intValue = 5;
double doubleValue = 3.14;
analyze(&intValue); // 整数指针版本
analyze(&doubleValue); // 浮点数指针版本
// 测试重载解析
cout << "\n6. 重载解析:" << endl;
test(5); // 精确匹配:test(int)
test(5.0); // 精确匹配:test(double)
test(5.0f); // 提升为double:test(double)
test('A'); // 提升为int:test(int)
// 测试歧义情况
cout << "\n7. 歧义测试:" << endl;
// compute(5); // 错误:歧义调用
// 通过强制类型转换解决歧义
compute(static_cast<double>(5)); // 明确调用某个版本
// 重载解析顺序:
cout << "\n=== 重载解析顺序 ===" << endl;
cout << "1. 精确匹配" << endl;
cout << "2. 提升转换(char→int, float→double)" << endl;
cout << "3. 标准转换(int→double, double→int)" << endl;
cout << "4. 用户定义转换" << endl;
cout << "5. 可变参数匹配" << endl;
return 0;
}
8.7 内联函数
8.7.1 内联函数基础
cpp
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
// 常规函数
int square(int x) {
return x * x;
}
// 内联函数
inline int cube(int x) {
return x * x * x;
}
// 内联函数可以定义在头文件中
inline int max(int a, int b) {
return (a > b) ? a : b;
}
inline double max(double a, double b) {
return (a > b) ? a : b;
}
// 内联函数也可以有多个语句
inline void swapValues(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 内联递归函数(编译器可能不内联)
inline int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
// 测试性能的函数
void testPerformance() {
const int ITERATIONS = 100000000;
// 测试常规函数
auto start = high_resolution_clock::now();
int sum1 = 0;
for (int i = 0; i < ITERATIONS; i++) {
sum1 += square(i % 100); // 函数调用
}
auto end = high_resolution_clock::now();
auto duration1 = duration_cast<milliseconds>(end - start);
// 测试内联函数
start = high_resolution_clock::now();
int sum2 = 0;
for (int i = 0; i < ITERATIONS; i++) {
sum2 += cube(i % 100); // 可能被内联
}
end = high_resolution_clock::now();
auto duration2 = duration_cast<milliseconds>(end - start);
// 测试直接计算(比较基准)
start = high_resolution_clock::now();
int sum3 = 0;
for (int i = 0; i < ITERATIONS; i++) {
int x = i % 100;
sum3 += x * x * x; // 直接计算
}
end = high_resolution_clock::now();
auto duration3 = duration_cast<milliseconds>(end - start);
cout << "性能测试结果 (迭代 " << ITERATIONS << " 次):" << endl;
cout << "常规函数: " << duration1.count() << " 毫秒" << endl;
cout << "内联函数: " << duration2.count() << " 毫秒" << endl;
cout << "直接计算: " << duration3.count() << " 毫秒" << endl;
}
int main() {
cout << "=== 内联函数 ===" << endl;
// 使用内联函数
cout << "5的立方: " << cube(5) << endl;
cout << "最大值(10, 20): " << max(10, 20) << endl;
cout << "最大值(3.14, 2.71): " << max(3.14, 2.71) << endl;
// 交换值
int a = 5, b = 10;
cout << "\n交换前: a=" << a << ", b=" << b << endl;
swapValues(a, b);
cout << "交换后: a=" << a << ", b=" << b << endl;
// 递归内联函数
cout << "\n5的阶乘: " << factorial(5) << endl;
// 性能测试
cout << "\n=== 性能测试 ===" << endl;
testPerformance();
// 内联函数的优缺点
cout << "\n=== 内联函数的优缺点 ===" << endl;
cout << "优点:" << endl;
cout << "1. 消除函数调用开销" << endl;
cout << "2. 编译器可以进行更好的优化" << endl;
cout << "3. 避免函数调用的参数压栈/出栈" << endl;
cout << "\n缺点:" << endl;
cout << "1. 代码膨胀(每个调用点都复制代码)" << endl;
cout << "2. 可能增加编译时间" << endl;
cout << "3. 调试困难(没有明显的函数调用)" << endl;
cout << "4. 复杂的函数不适合内联" << endl;
cout << "\n=== 使用建议 ===" << endl;
cout << "适合内联的函数:" << endl;
cout << "1. 非常小的函数(1-5行)" << endl;
cout << "2. 频繁调用的函数" << endl;
cout << "3. 简单的访问器和修改器" << endl;
cout << "\n不适合内联的函数:" << endl;
cout << "1. 递归函数" << endl;
cout << "2. 包含循环的函数" << endl;
cout << "3. 包含复杂逻辑的函数" << endl;
cout << "4. 虚函数(运行时多态)" << endl;
return 0;
}
8.7.2 内联与宏的对比
cpp
#include <iostream>
#include <cmath>
using namespace std;
// C风格宏(预处理阶段替换)
#define SQUARE_MACRO(x) ((x) * (x))
#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
#define SWAP_MACRO(type, a, b) { type temp = a; a = b; b = temp; }
// C++内联函数
inline int square_inline(int x) {
return x * x;
}
inline int max_inline(int a, int b) {
return (a > b) ? a : b;
}
template<typename T>
inline void swap_inline(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
// 测试宏的问题
void testMacroProblems() {
cout << "=== 宏的问题 ===" << endl;
// 问题1:参数多次求值
int counter = 0;
int result1 = SQUARE_MACRO(++counter);
cout << "SQUARE_MACRO(++counter): " << result1 << endl;
cout << "counter 现在的值: " << counter << " (应该是1,但实际是2)" << endl;
// 使用内联函数
counter = 0;
int result2 = square_inline(++counter);
cout << "\nsquare_inline(++counter): " << result2 << endl;
cout << "counter 现在的值: " << counter << " (正确:1)" << endl;
// 问题2:运算符优先级问题
int x = 2, y = 3;
int result3 = SQUARE_MACRO(x + y);
cout << "\nSQUARE_MACRO(x + y): " << result3 << endl;
cout << "预期: (2+3)*(2+3) = 25" << endl;
cout << "实际: " << result3 << " (正确,因为宏加了括号)" << endl;
// 如果没有括号会怎样?
#define SQUARE_BAD(x) x * x // 缺少括号
int result4 = SQUARE_BAD(x + y);
cout << "\nSQUARE_BAD(x + y): " << result4 << endl;
cout << "展开: " << x + y << " * " << x + y << " = " << result4 << endl;
cout << "错误!应该是 2+3*2+3 = 11" << endl;
// 问题3:类型不安全
double d1 = 2.5, d2 = 3.5;
double result5 = MAX_MACRO(d1, d2); // 可以工作
cout << "\nMAX_MACRO(2.5, 3.5): " << result5 << endl;
// 但可能有问题
int a = 5;
double b = 5.1;
double result6 = MAX_MACRO(a, b); // 混合类型
cout << "MAX_MACRO(5, 5.1): " << result6 << endl;
// 使用模板内联函数
cout << "\n使用模板内联函数: " << max_inline(a, static_cast<int>(b)) << endl;
// 问题4:调试困难
cout << "\n调试问题:" << endl;
int val1 = 10, val2 = 20;
cout << "交换前: val1=" << val1 << ", val2=" << val2 << endl;
// 使用宏交换
SWAP_MACRO(int, val1, val2);
cout << "交换后: val1=" << val1 << ", val2=" << val2 << endl;
// 调试时看不到SWAP_MACRO的实现
// 而内联函数可以在调试器中跟踪
// 使用内联函数交换
swap_inline(val1, val2);
cout << "再次交换: val1=" << val1 << ", val2=" << val2 << endl;
}
// 宏的独特用途(内联函数无法替代)
void testMacroAdvantages() {
cout << "\n=== 宏的独特优势 ===" << endl;
// 1. 字符串化
#define STRINGIFY(x) #x
cout << "字符串化宏: " << STRINGIFY(Hello World) << endl;
// 2. 连接
#define CONCAT(a, b) a##b
int xy = 100;
cout << "连接宏: CONCAT(x, y) = " << xy << endl;
// 3. 条件编译
#define DEBUG_MODE 1
#if DEBUG_MODE
cout << "调试模式启用" << endl;
#endif
// 4. 文件、行号信息(用于调试)
#define LOG(msg) cout << __FILE__ << ":" << __LINE__ << " " << msg << endl
LOG("这是一条日志消息");
// 5. 变参宏
#define PRINT_ARGS(...) cout << __VA_ARGS__ << endl
PRINT_ARGS("多个参数:", 1, 2, 3, "结束");
}
int main() {
cout << "=== 内联函数与宏的对比 ===" << endl;
testMacroProblems();
testMacroAdvantages();
cout << "\n=== 总结 ===" << endl;
cout << "宏的优点:" << endl;
cout << "1. 文本替换,非常灵活" << endl;
cout << "2. 支持字符串化、连接等高级功能" << endl;
cout << "3. 与预处理器的其他功能集成" << endl;
cout << "\n宏的缺点:" << endl;
cout << "1. 容易出错(运算符优先级、多次求值)" << endl;
cout << "2. 没有类型检查" << endl;
cout << "3. 调试困难" << endl;
cout << "4. 没有作用域" << endl;
cout << "\n内联函数的优点:" << endl;
cout << "1. 类型安全" << endl;
cout << "2. 调试方便" << endl;
cout << "3. 有作用域" << endl;
cout << "4. 编译器可以更好地优化" << endl;
cout << "\n内联函数的缺点:" << endl;
cout << "1. 不能做字符串化、连接等操作" << endl;
cout << "2. 不如宏灵活" << endl;
cout << "\n=== 现代C++建议 ===" << endl;
cout << "1. 优先使用内联函数代替宏进行计算" << endl;
cout << "2. 使用constexpr代替编译时常量宏" << endl;
cout << "3. 使用模板代替类型相关的宏" << endl;
cout << "4. 宏仅用于条件编译、日志等特殊用途" << endl;
return 0;
}
8.8 实践练习
练习1:数学函数库
cpp
/*
* 作业:创建数学函数库
* 功能:
* 1. 基本运算函数(加、减、乘、除、取模)
* 2. 数学函数(幂、平方根、绝对值、四舍五入)
* 3. 几何计算(圆面积、矩形面积、三角形面积)
* 4. 统计函数(平均值、方差、标准差)
* 5. 三角函数(sin, cos, tan,使用C++标准库)
*
* 要求:
* - 使用函数重载支持不同类型
* - 使用默认参数
* - 合理使用引用和值传递
* - 提供错误处理(如除零错误)
*/
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
int main() {
// 在这里编写你的代码
return 0;
}
练习2:学生成绩管理系统(函数版)
cpp
/*
* 作业:学生成绩管理系统(使用函数重构)
* 功能:
* 1. 添加学生信息(姓名、学号、成绩)
* 2. 显示所有学生信息
* 3. 查找学生(按姓名或学号)
* 4. 统计功能(平均分、最高分、最低分)
* 5. 排序功能(按成绩或姓名排序)
* 6. 保存/加载数据到文件
*
* 要求:
* - 每个功能实现为独立的函数
* - 使用结构体表示学生信息
* - 使用数组或vector存储多个学生
* - 实现清晰的操作菜单
* - 处理各种边界情况和错误输入
*/
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
int main() {
// 在这里编写你的代码
return 0;
}
练习3:日期时间工具函数
cpp
/*
* 作业:日期时间工具函数库
* 功能:
* 1. 验证日期合法性(考虑闰年)
* 2. 计算两个日期之间的天数差
* 3. 计算某日期是星期几
* 4. 日期加减运算(加N天、加N月、加N年)
* 5. 时间转换(24小时制↔12小时制)
* 6. 计算时间差
* 7. 显示当前日期时间
*
* 要求:
* - 使用结构体表示日期和时间
* - 实现完整的错误检查
* - 提供丰富的函数重载
* - 使用合理的默认参数
*/
#include <iostream>
#include <ctime>
#include <string>
using namespace std;
int main() {
// 在这里编写你的代码
return 0;
}
8.9 常见错误与调试
8.9.1 函数常见错误
cpp
#include <iostream>
#include <string>
using namespace std;
// 错误1:忘记返回语句
int add(int a, int b) {
int result = a + b;
// 忘记 return result;
// 编译器可能不会报错,但返回的值是未定义的
}
// 错误2:返回局部变量的引用/指针
int& badReturnReference() {
int local = 100;
return local; // 错误:返回局部变量的引用
}
int* badReturnPointer() {
int local = 100;
return &local; // 错误:返回局部变量的地址
}
// 错误3:函数声明与定义不匹配
// 声明
double calculate(int a, double b);
// 定义(参数类型不匹配)
double calculate(double a, int b) { // 错误!
return a + b;
}
// 错误4:递归函数没有终止条件
void infiniteRecursion(int n) {
cout << n << " ";
infiniteRecursion(n + 1); // 没有终止条件!
// 最终会导致栈溢出
}
// 错误5:默认参数位置错误
void wrongDefault(int a = 1, int b) { // 错误:默认参数必须在最后
cout << a << ", " << b << endl;
}
// 错误6:函数重载歧义
void ambiguous(int a) {
cout << "版本1" << endl;
}
void ambiguous(double a) {
cout << "版本2" << endl;
}
// 调用 ambiguous(5) 是合法的,但 ambiguous(5.0f) 可能产生歧义
// 错误7:未使用的参数(可能导致警告)
void process(int data) {
// 没有使用data参数
cout << "处理中..." << endl;
}
// 正确做法:明确标记未使用参数
void process2(int /*data*/) { // 注释掉参数名
cout << "处理中..." << endl;
}
void process3(int data) {
(void)data; // 显式忽略参数
cout << "处理中..." << endl;
}
// 错误8:数组作为参数时忘记传递大小
void printArray(int arr[]) { // 错误:不知道数组大小
// 无法安全地遍历数组
for (int i = 0; i < 10; i++) { // 假设大小是10
cout << arr[i] << " ";
}
}
// 正确做法
void printArrayCorrect(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
}
int main() {
cout << "=== 函数常见错误 ===" << endl;
// 演示错误1
cout << "\n错误1演示:" << endl;
int sum = add(5, 3);
cout << "add(5, 3) = " << sum << " (可能是垃圾值)" << endl;
// 演示错误2(危险!)
cout << "\n错误2演示(未定义行为):" << endl;
/*
int& ref = badReturnReference();
cout << "返回的引用值: " << ref << endl; // 未定义行为
int* ptr = badReturnPointer();
cout << "返回的指针值: " << *ptr << endl; // 未定义行为
*/
// 演示错误4:栈溢出
/*
cout << "\n错误4演示(栈溢出):" << endl;
infiniteRecursion(1); // 最终会崩溃
*/
// 演示错误6:重载歧义
cout << "\n错误6演示(重载歧义):" << endl;
ambiguous(5); // 明确调用int版本
ambiguous(5.0); // 明确调用double版本
// ambiguous(5.0f); // 可能产生歧义:float可以转int或double
// 演示错误8
cout << "\n错误8演示(数组大小问题):" << endl;
int smallArray[3] = {1, 2, 3};
// printArray(smallArray); // 错误:可能越界访问
printArrayCorrect(smallArray, 3); // 正确做法
cout << "\n=== 调试技巧 ===" << endl;
// 调试技巧1:添加日志输出
auto debugAdd = [](int a, int b) -> int {
cout << "[DEBUG] 调用add: a=" << a << ", b=" << b << endl;
int result = a + b;
cout << "[DEBUG] 返回结果: " << result << endl;
return result;
};
debugAdd(5, 3);
// 调试技巧2:使用断言
#include <cassert>
auto safeDivide = [](int a, int b) -> double {
assert(b != 0); // 调试时检查除数不为0
return static_cast<double>(a) / b;
};
cout << "\n10 / 2 = " << safeDivide(10, 2) << endl;
// cout << "10 / 0 = " << safeDivide(10, 0) << endl; // 断言失败
// 调试技巧3:分步执行
auto complexCalculation = [](int x) -> int {
// 分步计算,方便调试
int step1 = x * x;
cout << "步骤1: x² = " << step1 << endl;
int step2 = step1 + 5;
cout << "步骤2: x² + 5 = " << step2 << endl;
int step3 = step2 * 2;
cout << "步骤3: (x² + 5) × 2 = " << step3 << endl;
return step3;
};
cout << "\n分步调试演示:" << endl;
complexCalculation(3);
return 0;
}
恭喜! 你已经完成了第四部分的学习。在这一部分中,你掌握了:
- ✓ 函数基础:定义、声明、调用
- ✓ 参数传递:值传递、引用传递、指针传递
- ✓ 返回值:各种返回类型,返回引用/指针的注意事项
- ✓ 默认参数:使用方法与注意事项
- ✓ 函数重载:实现多态接口
- ✓ 内联函数:提高性能的优化技术
- ✓ 实践与调试:常见错误与解决方案
现在你已经能够编写模块化、可复用的代码了。在接下来的第五部分中,我们将学习复合数据类型,包括数组、字符串和结构体,这是构建复杂数据结构的基础。