8.1 C++ 内联函数(深入)
8.1.1 内联函数 vs 宏定义
内联函数是对 C 语言宏(#define)的改进,解决了宏的诸多问题。
cpp
// inline_vs_macro.cpp -- 内联函数与宏的对比
#include <iostream>
// C风格宏(有副作用!)
#define SQUARE_MACRO(x) ((x) * (x))
// C++内联函数(安全)
inline double square(double x)
{
return x * x;
}
inline int myMax(int a, int b)
{
return (a > b) ? a : b;
}
int main()
{
using namespace std;
int n = 5;
// 宏的副作用:n++被展开两次!
// SQUARE_MACRO(n++) → ((n++) * (n++)) → 5*6=30,n变为7
cout << "宏 SQUARE_MACRO(n++):" << SQUARE_MACRO(n++) << endl;
cout << "n 变为:" << n << endl; // 7(被加了两次!)
n = 5;
// 内联函数:n++只执行一次,结果正确
cout << "\n内联 square(n++):" << square(n++) << endl;
cout << "n 变为:" << n << endl; // 6(只加了一次)
// 内联函数有类型检查,宏没有
double d = 3.14;
cout << "\nsquare(3.14) = " << square(d) << endl; // 9.8596
return 0;
}
宏 vs 内联函数对比:
| 特性 | #define 宏 |
inline 函数 |
|---|---|---|
| 类型检查 | ❌ 无 | ✅ 有 |
| 调试支持 | ❌ 难以调试 | ✅ 可调试 |
| 副作用 | ❌ 参数可能多次求值 | ✅ 参数只求值一次 |
| 作用域 | ❌ 全局 | ✅ 遵循作用域规则 |
| 递归 | ❌ 不支持 | ✅ 支持(但不会内联) |
8.1.2 内联函数的使用场景
cpp
// inline_usage.cpp -- 内联函数适用场景
#include <iostream>
#include <cmath>
// ✅ 适合内联:函数体短小,频繁调用
inline double toRadians(double degrees)
{
return degrees * 3.14159265358979 / 180.0;
}
inline bool isPositive(double x) { return x > 0; }
inline int clamp(int val, int lo, int hi)
{
return (val < lo) ? lo : (val > hi) ? hi : val;
}
// ❌ 不适合内联:函数体过长
// inline void longFunction() { /* 100行代码 */ }
int main()
{
using namespace std;
cout << "30度 = " << toRadians(30) << " 弧度" << endl;
cout << "sin(30°) = " << sin(toRadians(30)) << endl;
cout << boolalpha;
cout << "isPositive(3.14) = " << isPositive(3.14) << endl;
cout << "isPositive(-1.0) = " << isPositive(-1.0) << endl;
// clamp:将值限制在[0, 100]范围内
cout << "clamp(150, 0, 100) = " << clamp(150, 0, 100) << endl; // 100
cout << "clamp(-10, 0, 100) = " << clamp(-10, 0, 100) << endl; // 0
cout << "clamp(75, 0, 100) = " << clamp(75, 0, 100) << endl; // 75
return 0;
}
8.2 引用变量(深入)
8.2.1 引用的本质与规则
cpp
// reference_deep.cpp -- 引用深入理解
#include <iostream>
int main()
{
using namespace std;
int a = 10;
int& ref = a; // ref 是 a 的别名
// 规则1:引用必须在声明时初始化
// int& r; // ❌ 错误
// 规则2:引用一旦绑定,不能改变绑定对象
int b = 20;
ref = b; // 这是赋值(a=20),不是改变绑定!
cout << "a = " << a << endl; // 20
cout << "ref = " << ref << endl; // 20(ref仍然是a的别名)
// 规则3:引用和原变量共享地址
cout << "&a = " << &a << endl;
cout << "&ref = " << &ref << endl; // 地址相同
// 规则4:不能有引用的引用,不能有指向引用的指针
// int&& rr = ref; // 这是右值引用(C++11),不同概念
int* p = &ref; // p 指向 a(通过ref取地址)
cout << "*p = " << *p << endl; // 20
return 0;
}
8.2.2 引用参数的三种使用场景
cpp
// reference_scenarios.cpp -- 引用参数的使用场景
#include <iostream>
#include <string>
// 场景1:修改调用者的变量(需要修改时用引用)
void increment(int& n, int step = 1)
{
n += step;
}
// 场景2:避免复制大对象(只读时用const引用)
void printString(const std::string& s)
{
std::cout << "字符串:" << s
<< "(长度:" << s.size() << ")" << endl;
}
// 场景3:返回多个值(通过引用参数"返回")
void minMax(const int arr[], int size, int& minVal, int& maxVal)
{
minVal = maxVal = arr[0];
for (int i = 1; i < size; i++)
{
if (arr[i] < minVal) minVal = arr[i];
if (arr[i] > maxVal) maxVal = arr[i];
}
}
int main()
{
using namespace std;
// 场景1
int x = 5;
increment(x); // x = 6
increment(x, 10); // x = 16
cout << "x = " << x << endl;
// 场景2
string longStr = "这是一个很长的字符串,通过const引用传递避免复制";
printString(longStr);
// 场景3
int data[] = {3, 1, 4, 1, 5, 9, 2, 6};
int minV, maxV;
minMax(data, 8, minV, maxV);
cout << "最小值:" << minV << " 最大值:" << maxV << endl;
return 0;
}
8.2.3 引用与临时变量
cpp
// reference_temp.cpp -- 引用与临时变量
#include <iostream>
// const引用可以绑定到临时变量(右值)
double refCube(const double& x)
{
return x * x * x;
}
// 非const引用不能绑定到临时变量
// double badFunc(double& x) { return x; } // 不能接受字面量
int main()
{
using namespace std;
// const引用可以接受字面量和表达式
cout << refCube(3.0) << endl; // ✅ 字面量
cout << refCube(3) << endl; // ✅ int自动转换为double临时变量
cout << refCube(2.0+1) << endl; // ✅ 表达式结果
// 非const引用只能接受左值(有地址的变量)
// badFunc(3.0); // ❌ 编译错误
double d = 3.0;
// badFunc(d); // ✅ 变量可以
return 0;
}
💡 关键规则:
- 非const引用 :只能绑定到左值(有名字、有地址的变量)
- const引用:可以绑定到左值、右值(字面量、临时变量、表达式)
8.2.4 返回引用
cpp
// return_ref.cpp -- 返回引用的正确与错误用法
#include <iostream>
#include <string>
struct Student {
std::string name;
int score;
};
// ✅ 正确:返回成员的引用(对象生命周期足够长)
std::string& getName(Student& s)
{
return s.name;
}
// ✅ 正确:返回静态变量的引用
std::string& getVersion()
{
static std::string version = "v1.0.0";
return version;
}
// ❌ 错误:返回局部变量的引用(悬空引用!)
// int& badReturn()
// {
// int local = 42;
// return local; // local在函数结束后销毁!
// }
int main()
{
using namespace std;
Student s = {"张三", 90};
// 返回引用可以作为左值
getName(s) = "李四"; // 直接修改s.name
cout << "修改后姓名:" << s.name << endl; // 李四
// 链式操作
getVersion() = "v2.0.0";
cout << "版本:" << getVersion() << endl;
return 0;
}
8.3 默认参数(深入)
cpp
// default_params_deep.cpp -- 默认参数深入
#include <iostream>
#include <string>
// 默认参数在原型中声明(不在定义中重复)
void createUser(std::string name,
int age = 18,
std::string role = "普通用户",
bool active = true);
int main()
{
using namespace std;
// 各种调用方式
createUser("张三"); // 全部使用默认值
createUser("李四", 25); // 指定age
createUser("王五", 30, "管理员"); // 指定age和role
createUser("赵六", 22, "VIP用户", false); // 全部指定
return 0;
}
void createUser(std::string name, int age,
std::string role, bool active)
{
using namespace std;
cout << "用户:" << name
<< " 年龄:" << age
<< " 角色:" << role
<< " 状态:" << (active ? "激活" : "禁用")
<< endl;
}
输出:
用户:张三 年龄:18 角色:普通用户 状态:激活
用户:李四 年龄:25 角色:普通用户 状态:激活
用户:王五 年龄:30 角色:管理员 状态:激活
用户:赵六 年龄:22 角色:VIP用户 状态:禁用
⚠️ 默认参数规则:
- 默认参数必须从右向左连续设置
- 一旦某个参数有默认值,其右边所有参数都必须有默认值
- 默认参数通常在函数原型中声明,定义中不重复
8.4 函数重载(深入)
8.4.1 重载的匹配规则
cpp
// overload_deep.cpp -- 函数重载深入
#include <iostream>
// 重载函数组
void show(int x)
{
std::cout << "show(int): " << x << std::endl;
}
void show(double x)
{
std::cout << "show(double): " << x << std::endl;
}
void show(int x, int y)
{
std::cout << "show(int,int): " << x << ", " << y << std::endl;
}
void show(const char* s)
{
std::cout << "show(const char*): " << s << std::endl;
}
int main()
{
using namespace std;
show(42); // show(int)
show(3.14); // show(double)
show(42, 100); // show(int, int)
show("Hello"); // show(const char*)
// 类型提升
short s = 10;
show(s); // show(int):short提升为int
float f = 1.5f;
show(f); // show(double):float提升为double
// 二义性错误示例(注释掉以避免编译错误)
// show('A'); // ❌ 二义性:char→int 还是 char→double?
return 0;
}
8.4.2 重载与 const 参数
cpp
// overload_const.cpp -- const与重载
#include <iostream>
// const版本和非const版本可以重载
void process(int& x)
{
std::cout << "非const引用版本:" << x << std::endl;
x *= 2;
}
void process(const int& x)
{
std::cout << "const引用版本:" << x << std::endl;
}
int main()
{
int a = 5;
const int b = 10;
process(a); // 调用非const版本(a是可修改的左值)
process(b); // 调用const版本(b是const)
process(3); // 调用const版本(字面量是右值,绑定到const引用)
std::cout << "a = " << a << std::endl; // 10(被修改了)
return 0;
}
8.4.3 何时不能重载
cpp
// 以下情况不构成重载(会导致编译错误):
// ❌ 仅返回类型不同,不构成重载
// int getValue() { return 1; }
// double getValue() { return 1.0; } // 错误!
// ❌ 参数名不同,不构成重载
// void func(int a) {}
// void func(int b) {} // 错误!重定义
// ✅ 参数类型不同,构成重载
void func(int a) {}
void func(double a) {}
// ✅ 参数数量不同,构成重载
void func(int a, int b) {}
8.5 函数模板
8.5.1 什么是函数模板?
函数模板允许编写与类型无关的通用函数,编译器根据调用时的参数类型自动生成具体函数。
模板定义格式:
template <typename T>
返回类型 函数名(T 参数, ...)
{
// 使用T作为类型
}
cpp
// template_basic.cpp -- 函数模板基础
#include <iostream>
// 函数模板:T是类型参数
template <typename T>
T myMax(T a, T b)
{
return (a > b) ? a : b;
}
template <typename T>
void swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template <typename T>
void printArray(const T arr[], int size)
{
for (int i = 0; i < size; i++)
std::cout << arr[i] << " ";
std::cout << std::endl;
}
int main()
{
using namespace std;
// 编译器根据参数类型自动实例化
cout << myMax(3, 5) << endl; // int版本:5
cout << myMax(3.14, 2.71) << endl; // double版本:3.14
cout << myMax('a', 'z') << endl; // char版本:z
int a = 10, b = 20;
swap(a, b);
cout << "交换后:a=" << a << " b=" << b << endl;
double da = 1.1, db = 2.2;
swap(da, db);
cout << "交换后:da=" << da << " db=" << db << endl;
int iarr[] = {1, 2, 3, 4, 5};
double darr[] = {1.1, 2.2, 3.3};
printArray(iarr, 5);
printArray(darr, 3);
return 0;
}
输出:
5
3.14
z
交换后:a=20 b=10
交换后:da=2.2 db=1.1
1 2 3 4 5
1.1 2.2 3.3
8.5.2 多类型参数模板
cpp
// multi_type_template.cpp -- 多类型参数模板
#include <iostream>
// 两个类型参数
template <typename T1, typename T2>
void printPair(T1 first, T2 second)
{
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
// 返回类型也是模板参数
template <typename T>
T sumArray(const T arr[], int size)
{
T sum = T(); // 默认初始化(0 for int/double)
for (int i = 0; i < size; i++)
sum += arr[i];
return sum;
}
int main()
{
using namespace std;
printPair(1, "hello"); // (1, hello)
printPair(3.14, true); // (3.14, 1)
printPair("name", "Alice"); // (name, Alice)
int iarr[] = {1, 2, 3, 4, 5};
double darr[] = {1.1, 2.2, 3.3, 4.4};
cout << "整数数组和:" << sumArray(iarr, 5) << endl; // 15
cout << "浮点数组和:" << sumArray(darr, 4) << endl; // 11
return 0;
}
8.5.3 模板的显式实例化与具体化
cpp
// template_specialization.cpp -- 模板具体化
#include <iostream>
#include <cstring>
// 通用模板
template <typename T>
bool isEqual(T a, T b)
{
return a == b;
}
// 模板具体化(针对特定类型提供特殊实现)
// 对 const char* 类型,用strcmp比较字符串内容
template <>
bool isEqual<const char*>(const char* a, const char* b)
{
return strcmp(a, b) == 0;
}
// 显式实例化(告诉编译器生成特定类型的版本)
template bool isEqual<int>(int, int); // 显式实例化int版本
template bool isEqual<double>(double, double); // 显式实例化double版本
int main()
{
using namespace std;
cout << boolalpha;
cout << "isEqual(1, 1) = " << isEqual(1, 1) << endl; // true
cout << "isEqual(1, 2) = " << isEqual(1, 2) << endl; // false
cout << "isEqual(1.0, 1.0) = " << isEqual(1.0, 1.0) << endl; // true
// 使用具体化版本比较字符串内容
const char* s1 = "hello";
const char* s2 = "hello";
const char* s3 = "world";
cout << "isEqual(\"hello\",\"hello\") = " << isEqual(s1, s2) << endl; // true
cout << "isEqual(\"hello\",\"world\") = " << isEqual(s1, s3) << endl; // false
return 0;
}
8.5.4 模板函数的重载
cpp
// template_overload.cpp -- 模板与普通函数重载
#include <iostream>
// 模板函数
template <typename T>
T add(T a, T b)
{
std::cout << "[模板] ";
return a + b;
}
// 普通函数(优先级高于模板)
int add(int a, int b)
{
std::cout << "[普通函数] ";
return a + b;
}
// 模板重载(不同参数数量)
template <typename T>
T add(T a, T b, T c)
{
std::cout << "[三参数模板] ";
return a + b + c;
}
int main()
{
using namespace std;
cout << add(1, 2) << endl; // 普通函数:3
cout << add(1.5, 2.5) << endl; // 模板(double):4
cout << add(1, 2, 3) << endl; // 三参数模板:6
cout << add(1.1, 2.2, 3.3)<< endl; // 三参数模板:6.6
return 0;
}
函数匹配优先级:
1. 完全匹配的普通函数(最高优先级)
2. 完全匹配的模板函数
3. 需要类型转换的普通函数
8.6 模板的局限性与解决方案
cpp
// template_limits.cpp -- 模板局限性
#include <iostream>
#include <string>
// 问题:对某些类型,模板的行为可能不正确
template <typename T>
T myMax(T a, T b)
{
return (a > b) ? a : b;
}
// 解决方案1:模板具体化
template <>
const char* myMax<const char*>(const char* a, const char* b)
{
return (strcmp(a, b) > 0) ? a : b;
}
// 解决方案2:使用string类(已重载>运算符)
// myMax<string> 可以直接使用
int main()
{
using namespace std;
cout << myMax(3, 5) << endl; // 5
cout << myMax(3.14, 2.71) << endl; // 3.14
// C风格字符串:使用具体化版本
const char* s1 = "banana";
const char* s2 = "apple";
cout << myMax(s1, s2) << endl; // banana(字典序更大)
// string类:直接使用模板
string str1 = "banana", str2 = "apple";
cout << myMax(str1, str2) << endl; // banana
return 0;
}
8.7 decltype 关键字(C++11)
decltype 用于推断表达式的类型,常与模板配合使用。
cpp
// decltype_demo.cpp -- decltype关键字
#include <iostream>
template <typename T1, typename T2>
// 问题:T1+T2的结果类型不确定(int+double=double)
// 解决:用decltype推断返回类型
auto addValues(T1 a, T2 b) -> decltype(a + b)
{
return a + b;
}
int main()
{
using namespace std;
// decltype推断变量类型
int x = 5;
double y = 3.14;
decltype(x) a = 10; // a 是 int 类型
decltype(y) b = 2.71; // b 是 double 类型
decltype(x+y) c = x + y; // c 是 double 类型(int+double=double)
cout << "a = " << a << " (int)" << endl;
cout << "b = " << b << " (double)" << endl;
cout << "c = " << c << " (double)" << endl;
// 模板中使用decltype
cout << "\naddValues(3, 4.5) = " << addValues(3, 4.5) << endl; // 7.5
cout << "addValues(1.5, 2.5) = " << addValues(1.5, 2.5) << endl; // 4
cout << "addValues(2, 3) = " << addValues(2, 3) << endl; // 5
return 0;
}
8.8 综合示例:通用排序与查找
cpp
// generic_algorithms.cpp -- 综合示例:通用算法
#include <iostream>
#include <string>
// ===== 通用排序(模板 + 函数指针)=====
template <typename T>
void bubbleSort(T arr[], int size, bool (*compare)(T, T))
{
for (int i = 0; i < size - 1; i++)
for (int j = 0; j < size - i - 1; j++)
if (!compare(arr[j], arr[j+1]))
std::swap(arr[j], arr[j+1]);
}
// 比较函数
template <typename T>
bool ascending(T a, T b) { return a <= b; }
template <typename T>
bool descending(T a, T b) { return a >= b; }
// ===== 通用查找(模板)=====
template <typename T>
int linearSearch(const T arr[], int size, const T& target)
{
for (int i = 0; i < size; i++)
if (arr[i] == target)
return i;
return -1; // 未找到
}
// ===== 通用打印(模板)=====
template <typename T>
void printArray(const T arr[], int size,
const std::string& label = "")
{
if (!label.empty())
std::cout << label << ":";
for (int i = 0; i < size; i++)
std::cout << arr[i] << " ";
std::cout << std::endl;
}
// ===== 通用统计(模板)=====
template <typename T>
struct Stats {
T minVal;
T maxVal;
double sum;
double average;
};
template <typename T>
Stats<T> calcStats(const T arr[], int size)
{
Stats<T> s;
s.minVal = s.maxVal = arr[0];
s.sum = arr[0];
for (int i = 1; i < size; i++)
{
if (arr[i] < s.minVal) s.minVal = arr[i];
if (arr[i] > s.maxVal) s.maxVal = arr[i];
s.sum += arr[i];
}
s.average = s.sum / size;
return s;
}
int main()
{
using namespace std;
// 整数数组
int iArr[] = {64, 34, 25, 12, 22, 11, 90};
int iSize = 7;
printArray(iArr, iSize, "原始整数数组");
bubbleSort(iArr, iSize, ascending<int>);
printArray(iArr, iSize, "升序排序后");
bubbleSort(iArr, iSize, descending<int>);
printArray(iArr, iSize, "降序排序后");
int idx = linearSearch(iArr, iSize, 25);
cout << "查找 25:位置 " << idx << endl;
auto iStats = calcStats(iArr, iSize);
cout << "最小值:" << iStats.minVal
<< " 最大值:" << iStats.maxVal
<< " 平均值:" << iStats.average << endl;
// 浮点数组
cout << "\n";
double dArr[] = {3.14, 1.41, 2.71, 1.73, 0.57};
int dSize = 5;
printArray(dArr, dSize, "原始浮点数组");
bubbleSort(dArr, dSize, ascending<double>);
printArray(dArr, dSize, "升序排序后");
auto dStats = calcStats(dArr, dSize);
cout << "最小值:" << dStats.minVal
<< " 最大值:" << dStats.maxVal
<< " 平均值:" << dStats.average << endl;
// 字符串数组
cout << "\n";
string sArr[] = {"banana", "apple", "cherry", "date", "elderberry"};
int sSize = 5;
printArray(sArr, sSize, "原始字符串数组");
bubbleSort(sArr, sSize, ascending<string>);
printArray(sArr, sSize, "字典序排序后");
int sIdx = linearSearch(sArr, sSize, string("cherry"));
cout << "查找 cherry:位置 " << sIdx << endl;
return 0;
}
输出:
原始整数数组:64 34 25 12 22 11 90
升序排序后:11 12 22 25 34 64 90
降序排序后:90 64 34 25 22 12 11
查找 25:位置 3
最小值:11 最大值:90 平均值:36.8571
原始浮点数组:3.14 1.41 2.71 1.73 0.57
升序排序后:0.57 1.41 1.73 2.71 3.14
最小值:0.57 最大值:3.14 平均值:1.912
原始字符串数组:banana apple cherry date elderberry
字典序排序后:apple banana cherry date elderberry
查找 cherry:位置 2
📝 第8章知识点总结
| 知识点 | 核心要点 |
|---|---|
| 内联函数 | 比宏更安全(有类型检查、无副作用),适合短小频繁调用的函数 |
| 引用深入 | 必须初始化,不能改变绑定;const引用可绑定临时变量 |
| 引用参数场景 | 修改变量用引用,只读大对象用const引用,返回多值用引用参数 |
| 返回引用 | 不能返回局部变量的引用,可返回成员/静态变量的引用 |
| 默认参数 | 从右向左设置,在原型中声明,调用时可省略右侧参数 |
| 函数重载 | 同名不同参数,返回类型不同不构成重载,const版本可重载 |
| 函数模板 | template <typename T> 定义通用函数,编译器自动实例化 |
| 模板具体化 | template <> 为特定类型提供特殊实现 |
| 模板重载 | 模板可以重载,普通函数优先级高于模板 |
| decltype | C++11新特性,推断表达式类型,常用于模板返回类型 |