指针:
定义:
指针是一个变量,它的值是另一个变量的内存地址。通过这个地址,我们可以间接访问或修改存储在那个地址的数据。
用途:
- 动态内存分配:通过
malloc
、calloc
、new
等函数或操作符在堆上分配内存,并返回该内存的首地址给指针。 - 函数参数传递:当需要传递大型数据结构(如数组、结构体)时,通常传递其指针以避免数据复制,提高性能。
- 链表、树等数据结构的实现:链表中的节点通常通过指针连接。
(this指针不能随意修改,因为它的值在成员函数定义时就已经确定了,指向当前对象。)
int x = 10;
int* ptr = &x; // ptr 现在指向 x 的内存地址
cout << *ptr; // 输出 10,解引用 ptr,访问 x 的值
野指针
野指针是一个指向已经被释放的内存的指针。当我们使用delete
或free
释放了一个指针所指向的内存后,该指针本身并不会自动变为nullptr
或NULL
,它仍然存储着原来的内存地址。如果之后我们尝试通过这个指针访问或修改内存,就会导致未定义的行为,这通常是非常危险的。
int* ptr = new int(10);
delete ptr; // 内存被释放,但ptr的值没有被置为nullptr
// ... 后续代码如果继续使用ptr,它就是一个野指针 ...
内存泄漏
定义:
当程序分配了内存但未能释放它时,就会发生内存泄漏。这通常发生在动态内存分配中,如使用 new
或 malloc
后忘记使用 delete
或 free
。
影响:
- 浪费内存:未释放的内存将一直被占用,直到程序结束。
- 性能下降:过多的内存泄漏可能导致系统性能下降,因为可用内存减少。
- 程序崩溃:在极端情况下,如果泄漏的内存过多,程序可能会因为无法分配更多内存而崩溃。
void leakMemory() {
int* ptr = new int(10); // 分配内存
// ... 忘记删除 ptr ...
} // 当函数结束时,ptr 指向的内存没有被释放,导致内存泄漏
智能指针
智能指针是C++提供的一种用于自动管理内存的机制。它们通过封装原始指针,并在适当的时候自动释放内存,从而避免了内存泄漏和野指针的问题。智能指针主要有以下几种类型:
-
unique_ptr:独占所有权的智能指针,同一时间只能有一个
unique_ptr
指向某个对象。当unique_ptr
被销毁时(例如离开作用域),它所指向的对象也会被自动删除。 -
shared_ptr:共享所有权的智能指针,允许多个
shared_ptr
指向同一个对象。当最后一个指向该对象的shared_ptr
被销毁时,对象才会被删除。 -
weak_ptr:对
shared_ptr
所管理的对象的一种弱引用,不会增加对象的引用计数。它用于解决shared_ptr
之间可能的循环引用问题。
#include <memory>
void safeFunction() {
std::unique_ptr<int> ptr(new int(10)); // 使用unique_ptr分配内存
// ... 无需显式delete ptr,当离开作用域时,ptr会自动删除其指向的内存 ...
} // 没有内存泄漏
注意:
智能指针share_ptr的好处在于,它可以自动管理内存,避免了手动释放内存的麻烦,并且可以避免内存泄漏和悬空指针等问题。
不可以使用同一个原始指针初始化不同的share_ptr,因为share_ptr是通过引用计数来管理内存的,如果使用同一个原始指针初始化多个share_ptr,会导致引用计数不正确,从而引发内存泄漏或悬空指针等问题。
weak_ptr的主要作用是解决循环引用问题,它不会增加引用计数,也不会阻止对象被销毁,可以避免因为循环引用而导致的内存泄漏问题。
unique_ptr中有拷贝构造函数,它的参数是unique_ptr类型的对象,用于将一个unique_ptr对象复制到另一个unique_ptr对象中。
-
总结:
- 指针是基础,它允许我们直接操作内存。但是,直接使用指针很容易出错,例如忘记释放内存导致内存泄漏,或者误用已经释放的指针(野指针)。
- 野指针和内存泄漏是错误使用指针可能导致的两个问题。野指针通常是由于指针指向的内存被释放后,仍然尝试访问该内存导致的;而内存泄漏则是由于分配的内存没有被释放,导致内存资源的浪费。
- 智能指针是为了解决野指针和内存泄漏问题而设计的。它们通过封装原始指针,并在适当的时候自动释放内存,从而减少了出错的可能性。智能指针是现代C++编程中推荐使用的内存管理方式。