C++学习第十课:指针陷阱与引用传递
在C++中,指针和引用是两个强大的特性,但它们也可能导致一些常见的错误。本课将深入探讨使用指针时可能遇到的问题,并介绍引用的概念和使用方式。
大纲标题与代码详解
1. 日常使用指针容易犯的错误
- 内存泄漏:未释放动态分配的内存。
int* ptr = new int(100);
// 忘记delete
- 指针无效:指针指向的内存已经被释放。
int* ptr = new int(100);
delete ptr;
// ptr变成野指针(悬挂指针),再访问*ptr是未定义行为
- 悬浮指针:指针被重新赋值前未置空。
int* ptr = new int(100);
int* oldPtr = ptr;
delete ptr;
ptr = nullptr; // ptr置空,oldPtr仍是悬挂指针
2. 检查 new
发出的分配请求是否得到满足
- 使用
new (std::nothrow)
避免异常,检查返回值。
int* ptr = new (std::nothrow) int(100);
if (ptr == nullptr) {
std::cerr << "Memory allocation failed" << std::endl;
}
3. 引用是什么
- 引用的基本概念:引用是别名,指向一个已存在的变量。
int original = 10;
int& ref = original; // ref是original的引用
ref = 20; // 修改引用,original的值也变为20
4. 按引用向函数传递参数
- 避免不必要的复制:尤其是对于大型对象或数组。
void printVector(std::vector<int>& vec) {
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
}
- 常量引用:防止函数内部修改参数值。
void printValue(const int& value) {
std::cout << value << std::endl;
}
5. 引用与指针的区别
- 安全性:引用必须在定义时就被初始化,不能是空的。
- 语法:引用在语法上更接近变量的使用,而指针则需要显式地使用解引用操作符
*
。
6. 引用在函数返回中的应用
- 通过引用返回多个值或返回大型对象,避免复制。
std::pair<int&, int&> getValues(int& a, int& b) {
return std::make_pair(a, b);
}
7. 引用在类设计中的应用
- 使用引用成员函数来避免对象复制。
class MyClass {
public:
void updateValue(int& value) {
value *= 2;
}
};
8. const
关键字与引用
- 使用
const
修饰引用,确保引用不会改变所绑定的变量的值。
void print(const int& value) {
// value不可修改
std::cout << value << std::endl;
}
练习题一:使用指针和动态内存管理实现简单内存池
目的:练习动态内存分配、指针操作和内存池概念。
任务:
- 创建一个名为
MemoryPool
的类,模拟一个简单的内存池。 - 类应该能够分配和释放内存。
- 分配内存时,应该检查是否有足够的空间,如果没有,则分配更多内存。
- 提供一个方法来访问分配的内存块。
示例代码框架:
#include <iostream>
#include <vector>
class MemoryPool {
private:
std::vector<char> memory; // 使用字符向量模拟内存
size_t blockSize; // 每个内存块的大小
public:
MemoryPool(size_t size) : blockSize(size) {}
// 分配内存块
void* allocateBlock() {
if (memory.size() < blockSize) {
// 如果当前内存不足以分配一个块,则扩展内存
memory.resize(memory.size() + blockSize);
}
// 返回当前内存的末尾指针,模拟分配
return &memory[memory.size() - blockSize];
}
// 释放内存块(简单起见,这里不实现真正的释放逻辑)
void releaseBlock(void* ptr) {
// 检查ptr是否在memory范围内,并执行必要的释放逻辑
}
// 获取内存块的数据(用于测试)
char* getData(void* ptr) {
return static_cast<char*>(ptr);
}
};
int main() {
MemoryPool pool(10); // 创建一个每个块大小为10的内存池
void* block1 = pool.allocateBlock();
void* block2 = pool.allocateBlock();
// 使用getData方法测试访问内存块
*pool.getData(block1) = 'A';
*(pool.getData(block1) + 1) = '\0';
std::cout << "Block 1 contains: " << pool.getData(block1) << std::endl;
pool.releaseBlock(block1);
pool.releaseBlock(block2);
return 0;
}
练习题二:实现一个函数,通过引用传递参数并修改原值
目的:练习引用的使用,理解引用与指针的区别。
任务:
- 编写一个函数
increment
,它接受一个整数的引用,并将其值加一。 - 在
main
函数中测试increment
函数,确保它能够修改传入的整数变量的值。
示例代码框架:
#include <iostream>
// 函数通过引用修改整数的值
void increment(int& value) {
value += 1;
}
int main() {
int number = 10;
std::cout << "Before increment: " << number << std::endl;
increment(number);
std::cout << "After increment: " << number << std::endl;
return 0;
}
注意:
- 在
increment
函数中,int& value
表示value
是一个整数的引用。 - 修改
value
的值将直接影响传递给函数的原始变量。
结语
在本课中,我们学习了使用指针时可能遇到的问题,包括内存泄漏、无效指针和悬浮指针。同时,我们深入了解了引用的概念和应用,包括如何通过引用传递参数以避免不必要的复制,以及如何在函数中使用引用返回多个值。
引用提供了一种安全、高效的方式来传递变量,而不需要担心复制大型对象或数组的性能开销。正确使用指针和引用,可以帮助我们编写出更加高效和安全的C++程序。