指针用于访问程序外部的资源------如堆内存。因此,访问堆内存(如果在堆中创建了任何内容)时,需要使用指针。当访问任何外部资源时,我们只使用该资源的副本。如果我们对其进行修改,我们只会改变副本的版本。但如果我们使用指针来访问资源,我们将能够修改原始资源。
C++ 中普通指针的一些问题如下:
-
内存泄漏 :当程序反复分配内存但从未释放时,会导致内存泄漏。这会导致过度的内存消耗,最终可能导致系统崩溃。
-
悬空指针 :悬空指针是指在对象从内存中被释放后,没有修改指针的值。此时,指针仍然指向已释放的内存。
-
野指针 :野指针是已经声明并分配了内存的指针,但该指针从未初始化为指向有效的对象或地址。
-
数据不一致:数据不一致发生在一些数据存储在内存中,但没有以一致的方式更新。
cpp
// C++ 程序演示指针的工作方式
#include <iostream>
using namespace std;
class Rectangle {
private:
int length;
int breadth;
};
void fun() {
// 使用指针 p 并动态创建一个 Rectangle 对象
Rectangle* p = new Rectangle();
}
int main() {
// 无限循环
while (1) {
fun();
}
}
输出
bash
Memory limit exceeded
解释:
在 fun
函数中,创建了一个指针 p
,它指向一个 Rectangle
对象。这个对象包含两个整数:length
和 breadth
。当 fun
函数结束时,指针 p
会被销毁,因为它是一个局部变量。然而,它占用的内存并没有被释放,因为我们忘记使用 delete p;
来删除它。这意味着内存不会被释放,无法供其他资源使用。虽然我们不再需要这个变量,但我们需要释放内存。
在 main
函数中,fun
函数被无限次调用。每次调用都会创建 p
,但内存没有被释放。随着调用的进行,内存不断增加,但不会被回收。由于没有释放的内存,最终会导致内存泄漏,整个堆内存可能因此变得无用。
智能指针
正如我们不自觉地发现的那样,未释放指针会导致内存泄漏,可能会导致程序崩溃。Java、C# 等语言通过垃圾回收机制来智能地释放未使用的内存。C++ 也有自己的机制:智能指针 。当对象被销毁时,智能指针会自动释放内存。因此,我们不需要手动调用 delete
来释放内存。
智能指针是一个指针的封装类,重载了像 *
和 ->
等操作符。智能指针类的对象看起来像普通指针,但与普通指针不同,它可以释放销毁的对象内存。
智能指针的思想是创建一个包含指针、析构函数和重载操作符(如 *
和 ->
)的类。由于析构函数会在对象超出作用域时自动调用,因此动态分配的内存会自动被删除(或者引用计数会减少)。
cpp
// C++ 程序演示智能指针的工作方式
#include <iostream>
using namespace std;
class SmartPtr {
int* ptr; // 实际指针
public:
// 构造函数
explicit SmartPtr(int* p = NULL) { ptr = p; }
// 析构函数
~SmartPtr() { delete (ptr); }
// 重载解引用操作符
int& operator*() { return *ptr; }
};
int main() {
SmartPtr ptr(new int());
*ptr = 20;
cout << *ptr;
// 我们不需要调用 delete ptr:当对象 ptr 超出作用域时
// 它的析构函数会自动被调用,析构函数会删除 ptr。
return 0;
}
输出
cpp
20
指针与智能指针的区别
指针 | 智能指针 |
---|---|
指针是一个存储内存地址和该内存位置数据类型信息的变量。指针是指向内存中某个位置的变量。 | 智能指针是一个指针封装的栈分配对象。简单来说,智能指针是封装指针的类。 |
它不会在作用域结束时销毁。 | 它在作用域结束时会销毁自己。 |
指针没有额外的特性,效率较低。 | 智能指针效率更高,因为它具有内存管理的附加功能。 |
指针是手动管理的。 | 智能指针是自动管理的。 |
注意:
这仅适用于 int
类型。那我们必须为每个对象创建智能指针吗?不,解决方案是模板。如下所示,T
可以是任何类型。
示例:使用模板解决问题
cpp
// C++ 程序演示模板的工作方式,并解决指针问题
#include <iostream>
using namespace std;
// 通用智能指针类
template <class T> class SmartPtr {
T* ptr; // 实际指针
public:
// 构造函数
explicit SmartPtr(T* p = NULL) { ptr = p; }
// 析构函数
~SmartPtr() { delete (ptr); }
// 重载解引用操作符
T& operator*() { return *ptr; }
// 重载箭头操作符,这样可以像指针一样访问 T 的成员
T* operator->() { return ptr; }
};
int main() {
SmartPtr<int> ptr(new int());
*ptr = 20;
cout << *ptr;
return 0;
}
输出
cpp
20
注意:
智能指针也非常适用于资源管理,比如文件句柄或网络套接字等。
智能指针的类型
C++ 库提供了以下类型的智能指针实现:
-
auto_ptr
-
unique_ptr
-
shared_ptr
-
weak_ptr
auto_ptr
使用 auto_ptr
,可以管理通过 new
表达式获取的对象,并在 auto_ptr
自身销毁时删除它们。当通过 auto_ptr
描述一个对象时,它会存储指向单个分配对象的指针。
注意:从 C++11 起,
auto_ptr
被弃用。unique_ptr
是一个类似的功能,但它提供了更高的安全性。
unique_ptr
unique_ptr
只存储一个指针。我们可以通过移除当前对象并指向另一个对象来重新赋值。
实例
cpp
// C++ 程序演示 unique_ptr 的工作方式
// 这里我们展示 unique_ptr 指向 P1。
// 但是我们移除了 P1 并将其指向 P2,因此指针现在
// 指向 P2。
#include <iostream>
using namespace std;
// 动态内存管理库
#include <memory>
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area() { return length * breadth; }
};
int main()
{
// 智能指针
unique_ptr<Rectangle> P1(new Rectangle(10, 5));
cout << P1->area() << endl; // 打印 50
// unique_ptr<Rectangle> P2(P1);
unique_ptr<Rectangle> P2;
P2 = move(P1);
cout << P2->area() << endl;
return 0;
}
cpp
50
50
shared_ptr
通过 shared_ptr
,多个指针可以同时指向同一个对象,它将使用 use_count()
方法来维护引用计数。
cpp
// C++ 程序演示 shared_ptr 的工作方式
// 这里智能指针 P1 和 P2 都指向同一个
// 对象,并且它们都会保持该对象的引用。
#include <iostream>
using namespace std;
// 动态内存管理库
#include <memory>
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area() { return length * breadth; }
};
int main()
{
// 智能指针
shared_ptr<Rectangle> P1(new Rectangle(10, 5));
cout << P1->area() << endl;
shared_ptr<Rectangle> P2;
P2 = P1;
cout << P2->area() << endl;
cout << P1->area() << endl;
cout << P1.use_count() << endl;
return 0;
}
cpp
50
50
50
2
weak_ptr
weak_ptr
是一种智能指针,它持有一个非拥有的引用。它与 shared_ptr
非常相似,但不会维护引用计数。这样,指针不会对对象保持强引用,避免了通过 shared_ptr
创建的循环依赖问题。
cpp
// C++ 程序演示 weak_ptr 的工作方式
// 这里智能指针 P1 和 P2 都指向同一个
// 对象,但它们都不保持对象的引用。
#include <iostream>
using namespace std;
// 动态内存管理库
#include <memory>
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area() { return length * breadth; }
};
int main()
{
// 智能指针
shared_ptr<Rectangle> P1(new Rectangle(10, 5));
// 创建 weak_ptr
weak_ptr<Rectangle> P2(P1);
cout << P1->area() << endl;
cout << P1.use_count() << endl;
return 0;
}
cpp
50
1