这一章主要讲解C++中的函数,这是构建模块化、可重用代码的基础。
6.1 函数基础
函数定义和调用
cpp
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
// 1. 函数声明(函数原型)
int factorial(int n);
void printMessage(const string& message);
// 2. 函数定义
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
void printMessage(const string& message) {
cout << "消息: " << message << endl;
}
// 3. 参数列表可以为空
void sayHello() {
cout << "Hello, World!" << endl;
}
int main()
{
// 函数调用
sayHello();
printMessage("欢迎学习C++函数!");
int num = 5;
int result = factorial(num);
cout << num << "! = " << result << endl;
return 0;
}
局部对象和作用域
cpp
#include <iostream>
using std::cout;
using std::endl;
// 全局变量
int globalVar = 100;
void demonstrateScope() {
// 局部变量
int localVar = 50;
static int staticVar = 0; // 静态局部变量
cout << "函数内 - 局部变量: " << localVar << endl;
cout << "函数内 - 静态变量: " << staticVar << endl;
cout << "函数内 - 全局变量: " << globalVar << endl;
localVar++;
staticVar++;
globalVar++;
}
int main()
{
cout << "第一次调用:" << endl;
demonstrateScope();
cout << "\n第二次调用:" << endl;
demonstrateScope();
cout << "\n主函数内 - 全局变量: " << globalVar << endl;
// cout << localVar << endl; // 错误!localVar不可访问
return 0;
}
6.2 参数传递
传值参数
cpp
#include <iostream>
using std::cout;
using std::endl;
// 传值参数:形参是实参的副本
void passByValue(int x) {
x = x * 2; // 只修改副本,不影响原值
cout << "函数内 x = " << x << endl;
}
// 指针参数:可以修改指向的对象
void passByPointer(int* ptr) {
if (ptr) { // 检查指针是否有效
*ptr = *ptr * 2; // 修改指针指向的值
cout << "函数内 *ptr = " << *ptr << endl;
}
}
int main()
{
int a = 10;
cout << "传值调用前: a = " << a << endl;
passByValue(a);
cout << "传值调用后: a = " << a << endl;
cout << "\n传指针调用前: a = " << a << endl;
passByPointer(&a);
cout << "传指针调用后: a = " << a << endl;
return 0;
}
传引用参数
cpp
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
// 传引用参数:形参是实参的别名
void passByReference(int& ref) {
ref = ref * 2; // 直接修改原值
cout << "函数内 ref = " << ref << endl;
}
// 使用引用避免拷贝大型对象
void printLargeObject(const string& str) {
cout << "字符串: " << str << endl;
// str[0] = 'H'; // 错误!const引用不能修改
}
// 使用引用返回多个值
void calculate(int a, int b, int& sum, int& product) {
sum = a + b;
product = a * b;
}
// 引用参数的重载
void processValue(int x) {
cout << "处理值: " << x << endl;
}
void processValue(int& x) {
cout << "处理引用: " << x << endl;
x++; // 可以修改原值
}
int main()
{
int num = 5;
cout << "传引用调用前: num = " << num << endl;
passByReference(num);
cout << "传引用调用后: num = " << num << endl;
// 使用引用返回多个值
int x = 10, y = 20;
int sum, product;
calculate(x, y, sum, product);
cout << x << " + " << y << " = " << sum << endl;
cout << x << " * " << y << " = " << product << endl;
// 引用重载
int value = 100;
processValue(value); // 调用引用版本
processValue(value + 1); // 调用传值版本(临时对象)
return 0;
}
const参数和返回值
cpp
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
// const引用参数:避免拷贝,同时防止修改
bool findValue(const vector<int>& vec, int value) {
for (const auto& elem : vec) {
if (elem == value) {
return true;
}
}
return false;
}
// 返回const引用(要确保引用对象在函数返回后仍然存在)
const string& getLongerString(const string& s1, const string& s2) {
return s1.size() > s2.size() ? s1 : s2;
}
// 返回指向常量的指针
const int* findMaxElement(const int arr[], size_t size) {
if (size == 0) return nullptr;
const int* maxPtr = &arr[0];
for (size_t i = 1; i < size; ++i) {
if (arr[i] > *maxPtr) {
maxPtr = &arr[i];
}
}
return maxPtr;
}
int main()
{
vector<int> numbers = {1, 3, 5, 7, 9};
int target = 5;
if (findValue(numbers, target)) {
cout << "找到 " << target << endl;
} else {
cout << "未找到 " << target << endl;
}
string str1 = "hello";
string str2 = "worldwide";
const string& longer = getLongerString(str1, str2);
cout << "较长的字符串: " << longer << endl;
int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
const int* maxElement = findMaxElement(arr, 8);
if (maxElement) {
cout << "最大元素: " << *maxElement << endl;
}
return 0;
}
6.3 返回类型和return语句
值返回
cpp
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
// 返回内置类型
int square(int x) {
return x * x; // 返回值
}
// 返回类类型(可能涉及拷贝)
string getGreeting(bool formal) {
if (formal) {
return "Good day"; // 返回临时string对象
} else {
return "Hi"; // 返回另一个临时string对象
}
}
// 返回引用(必须确保引用对象仍然存在)
const int& getElement(const vector<int>& vec, size_t index) {
if (index >= vec.size()) {
throw std::out_of_range("索引越界");
}
return vec[index]; // 返回const引用
}
// 返回列表(C++11)
vector<int> getNumbers() {
return {1, 2, 3, 4, 5}; // 返回初始化列表
}
// 主函数返回类型
int main() {
cout << "5的平方: " << square(5) << endl;
cout << "正式问候: " << getGreeting(true) << endl;
vector<int> nums = {10, 20, 30};
const int& elem = getElement(nums, 1);
cout << "第二个元素: " << elem << endl;
auto numbers = getNumbers();
cout << "数字列表: ";
for (auto n : numbers) {
cout << n << " ";
}
cout << endl;
return 0; // 主函数返回0表示成功
}
引用返回
cpp
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
// 返回非常量引用,允许修改
int& getElement(vector<int>& vec, size_t index) {
return vec[index]; // 返回引用,可以修改原元素
}
// 错误的引用返回:返回局部变量的引用
// int& badFunction() {
// int local = 42;
// return local; // 错误!local在函数结束时被销毁
// }
// 返回静态局部变量的引用
int& getCounter() {
static int counter = 0; // 静态变量在程序结束时才销毁
return counter;
}
int main()
{
vector<int> numbers = {1, 2, 3, 4, 5};
cout << "修改前: ";
for (auto n : numbers) {
cout << n << " ";
}
cout << endl;
// 通过返回的引用修改元素
getElement(numbers, 2) = 100;
cout << "修改后: ";
for (auto n : numbers) {
cout << n << " ";
}
cout << endl;
// 使用计数器
getCounter() = 10;
cout << "计数器: " << getCounter() << endl;
getCounter() += 5;
cout << "计数器: " << getCounter() << endl;
return 0;
}
6.4 函数重载
cpp
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
// 函数重载:同名函数,不同参数列表
// 1. 参数类型不同
void print(int value) {
cout << "整数: " << value << endl;
}
void print(double value) {
cout << "浮点数: " << value << endl;
}
void print(const string& value) {
cout << "字符串: " << value << endl;
}
// 2. 参数数量不同
void show() {
cout << "无参数" << endl;
}
void show(int a) {
cout << "一个参数: " << a << endl;
}
void show(int a, int b) {
cout << "两个参数: " << a << ", " << b << endl;
}
// 3. 参数类型修饰不同
void process(int x) { // 传值
cout << "传值: " << x << endl;
}
void process(int& x) { // 传引用
cout << "传引用: " << x << endl;
x++;
}
void process(const int& x) { // 传常量引用
cout << "传常量引用: " << x << endl;
}
// 4. 默认参数(与重载相关)
void display(string message, bool newline = true) {
cout << message;
if (newline) {
cout << endl;
}
}
int main()
{
// 重载函数调用
print(42); // 调用print(int)
print(3.14); // 调用print(double)
print("hello"); // 调用print(const string&)
show(); // 调用show()
show(10); // 调用show(int)
show(20, 30); // 调用show(int, int)
int num = 5;
process(num); // 调用process(int&)
process(100); // 调用process(const int&)
process(static_cast<const int&>(num)); // 调用process(const int&)
// 默认参数
display("第一行");
display("第二行", true);
display("没有换行", false);
cout << "这是新的一行" << endl;
return 0;
}
6.5 特殊用途语言特性
默认实参
cpp
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
// 默认实参:在参数列表中指定默认值
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;
}
// 默认实参只能从右向左提供
void func(int a, int b = 10, int c = 20) {
cout << "a=" << a << ", b=" << b << ", c=" << c << endl;
}
// 错误:默认实参不在最后
// void badFunc(int a = 5, int b) { } // 编译错误
int main()
{
// 使用不同数量的参数调用
createWindow(); // 使用所有默认值
createWindow("My App"); // 只提供标题
createWindow("Game", 1024); // 提供标题和宽度
createWindow("Fullscreen App", 1920, 1080, true); // 提供所有参数
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;
}
内联函数和constexpr函数
cpp
#include <iostream>
using std::cout;
using std::endl;
// 内联函数:建议编译器在调用点展开函数体
inline int max(int a, int b) {
return a > b ? a : b;
}
// constexpr函数:用于常量表达式,在编译时求值;
// 但是:如果编译时n的值还不知道,就会在运行是求值。
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int square(int x) {
return x * x;
}
// 调试用的内联函数
inline void debugPrint(const char* message) {
#ifdef DEBUG
cout << "[DEBUG] " << message << endl;
#endif
}
int main()
{
// 内联函数调用
int a = 5, b = 10;
cout << "最大值: " << max(a, b) << endl;
// constexpr函数调用
constexpr int size = factorial(5); // 编译时计算
cout << "5! = " << size << endl;
int arr[square(3)]; // 数组大小在编译时确定
cout << "数组大小: " << sizeof(arr) / sizeof(arr[0]) << endl;
// 运行时也可以调用constexpr函数
int x = 4;
cout << x << "! = " << factorial(x) << endl;
debugPrint("程序启动");
return 0;
}
6.6 函数匹配
cpp
#include <iostream>
using std::cout;
using std::endl;
void f() {
cout << "f()" << endl;
}
void f(int a) {
cout << "f(int)" << endl;
}
void f(int a, int b) {
cout << "f(int, int)" << endl;
}
void f(double a, double b = 3.14) {
cout << "f(double, double)" << endl;
}
void example(int a, double b) {
cout << "example(int, double)" << endl;
}
void example(double a, int b) {
cout << "example(double, int)" << endl;
}
int main()
{
// 精确匹配
f(5); // f(int)
// 类型转换匹配
f(5.0); // f(double, double) - double到int需要转换
// 二义性调用
// f(5, 5.0); // 错误!二义性:f(int, int) 或 f(double, double)
// 通过强制转换解决二义性
f(static_cast<double>(5), 5.0); // f(double, double)
// 另一个二义性例子
// example(5, 5); // 错误!二义性:需要int->double或double->int转换
// 默认参数的影响
f(5.0); // 调用f(double, double),使用默认参数
return 0;
}
6.7 函数指针(含函数指针数组)
cpp
#include <iostream>
#include <vector>
#include <cmath>
using std::cout;
using std::endl;
using std::vector;
// 普通函数
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
double squareRoot(double x) {
return std::sqrt(x);
}
// 使用函数指针作为参数
void processNumbers(const vector<int>& numbers, int (*operation)(int, int), int init) {
int result = init;
for (size_t i = 0; i < numbers.size(); ++i) {
result = operation(result, numbers[i]);
}
cout << "处理结果: " << result << endl;
}
// 使用typedef/using简化函数指针类型
using MathOperation = int(*)(int, int);
void calculate(MathOperation op, int a, int b) {
int result = op(a, b);
cout << "计算结果: " << result << endl;
}
int main()
{
// 函数指针声明和赋值
int (*funcPtr)(int, int);
funcPtr = add; // 指向add函数
cout << "加法: " << funcPtr(5, 3) << endl;
funcPtr = multiply; // 指向multiply函数
cout << "乘法: " << funcPtr(5, 3) << endl;
// 函数指针数组
int (*operations[])(int, int) = {add, multiply};
cout << "操作0: " << operations[0](5, 3) << endl; // add
cout << "操作1: " << operations[1](5, 3) << endl; // multiply
// 函数指针作为参数
vector<int> numbers = {1, 2, 3, 4, 5};
processNumbers(numbers, add, 0); // 求和
processNumbers(numbers, multiply, 1); // 求积
// 使用类型别名
calculate(add, 10, 20);
calculate(multiply, 10, 20);
return 0;
}
📝 第六章关键要点总结
-
参数传递方式:
- 传值:创建副本,不影响原值
- 传引用:操作原对象,避免拷贝
- 传const引用:只读访问,高效安全
-
返回类型选择:
- 值返回:返回副本
- 引用返回:返回别名,要确保对象生命周期
- 不要返回局部变量的引用或指针
-
函数设计原则:
- 单一职责:一个函数只做一件事
- 合理重载:参数列表应该明显不同
- 使用默认参数简化接口
-
特殊函数:
- 内联函数:小型频繁调用的函数
- constexpr函数:编译时求值的函数
这一章是C++编程的重要基础,函数是代码重用的关键。多练习函数的设计和使用,为后续面向对象编程打下坚实基础!