智能指针是C++中管理动态内存的强大工具,它们通过自动化的内存管理机制大大减少了内存泄漏和悬空指针的风险。下面我将详细解释各种智能指针的使用格式及其核心成员函数。
创建格式:
// 方式1:直接构造
std::unique_ptr<类型> ptr(new 类型(构造参数));
// 方式2:使用std::make_unique (C++14起)
auto ptr = std::make_unique<类型>(构造参数);
示例:
// 创建管理int的unique_ptr
std::unique_ptr<int> numPtr(new int(42));
// 创建管理自定义类的unique_ptr
class MyClass { /*...*/ };
auto objPtr = std::make_unique<MyClass>(constructorArgs);
创建格式:
// 方式1:直接构造
std::shared_ptr<类型> ptr(new 类型(构造参数));
// 方式2:使用std::make_shared (推荐)
auto ptr = std::make_shared<类型>(构造参数);
示例:
// 创建shared_ptr
auto sharedNum = std::make_shared<int>(100);
// 从unique_ptr转移所有权
std::unique_ptr<int> uniqueNum = std::make_unique<int>(50);
std::shared_ptr<int> sharedFromUnique = std::move(uniqueNum);
创建格式:
// 必须从shared_ptr构造
std::weak_ptr<类型> weakPtr = sharedPtr;
示例:
auto shared = std::make_shared<std::string>("Hello");
std::weak_ptr<std::string> weak(shared);
函数/操作 | 说明 | 示例 |
---|---|---|
get() | 返回管理的原始指针 | int* raw = uptr.get(); |
reset() | 释放当前对象,可接管新对象 | uptr.reset(); 或 uptr.reset(new int(5)); |
release() | 释放所有权,返回原始指针 | int* raw = uptr.release(); |
operator-> | 访问成员 | uptr->method(); |
operator* | 解引用 | int val = *uptr; |
operator bool | 检查是否为空 | if(uptr) { ... } |
swap() | 交换两个unique_ptr | uptr1.swap(uptr2); |
完整示例:
#include <memory>
#include <iostream>
int main() {
// 创建unique_ptr
std::unique_ptr<int> uptr = std::make_unique<int>(10);
// 使用get获取原始指针
std::cout << "Value: " << *uptr.get() << "\n";
// 解引用操作
*uptr = 20;
std::cout << "Modified value: " << *uptr << "\n";
// 重置指针
uptr.reset(new int(30));
std::cout << "After reset: " << *uptr << "\n";
// 释放所有权
int* raw = uptr.release();
std::cout << "After release, uptr is " << (uptr ? "not null" : "null") << "\n";
delete raw;
return 0;
}
函数/操作 | 说明 | 示例 |
---|---|---|
use_count() | 返回引用计数 | size_t count = sptr.use_count(); |
unique() | 检查是否是唯一所有者 | if(sptr.unique()) {...} |
reset() | 释放当前对象,减少引用计数 | sptr.reset(); |
get() | 返回管理的原始指针 | auto raw = sptr.get(); |
operator-> | 访问成员 | sptr->method(); |
operator* | 解引用 | auto val = *sptr; |
operator bool | 检查是否为空 | if(sptr) {...} |
swap() | 交换两个shared_ptr | sptr1.swap(sptr2); |
完整示例:
#include <memory>
#include <iostream>
class Resource {
public:
Resource() { std::cout << "Resource created\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
void use() { std::cout << "Resource used\n"; }
};
int main() {
// 创建shared_ptr
std::shared_ptr<Resource> sptr1 = std::make_shared<Resource>();
// 复制shared_ptr
{
auto sptr2 = sptr1;
std::cout << "Use count inside block: " << sptr1.use_count() << "\n";
sptr2->use();
}
std::cout << "Use count outside block: " << sptr1.use_count() << "\n";
// 重置shared_ptr
sptr1.reset();
std::cout << "After reset, use count: " << sptr1.use_count() << "\n";
return 0;
}
函数/操作 | 说明 | 示例 |
---|---|---|
lock() | 创建共享指针来访问对象 | auto sptr = wptr.lock(); |
expired() | 检查对象是否已被释放 | if(wptr.expired()) {...} |
use_count() | 返回关联shared_ptr的引用计数 | auto count = wptr.use_count(); |
reset() | 释放观察的对象 | wptr.reset(); |
swap() | 交换两个weak_ptr | wptr1.swap(wptr2); |
完整示例:
#include <memory>
#include <iostream>
int main() {
// 创建shared_ptr
auto shared = std::make_shared<int>(42);
// 创建weak_ptr
std::weak_ptr<int> weak = shared;
// 检查weak_ptr
if (!weak.expired()) {
if (auto locked = weak.lock()) {
std::cout << "Value through weak_ptr: " << *locked << "\n";
std::cout << "Use count: " << weak.use_count() << "\n";
}
}
// 释放shared_ptr
shared.reset();
// 再次检查
if (weak.expired()) {
std::cout << "Object has been destroyed\n";
}
return 0;
}
所有智能指针都支持比较操作:
std::shared_ptr<int> p1, p2;
if (p1 == p2) { ... } // 比较管理的指针
if (p1 < p2) { ... } // 基于管理指针的排序
标准库提供了专门用于智能指针的swap函数:
std::swap(ptr1, ptr2); // 等价于 ptr1.swap(ptr2)
用于在关联容器中比较智能指针的所有权:
std::map<std::shared_ptr<int>, int, std::owner_less<std::shared_ptr<int>>> myMap;
智能指针允许指定自定义的删除器,格式如下:
// 函数指针形式
void IntDeleter(int* p) { delete p; }
std::unique_ptr<int, decltype(&IntDeleter)> uptr(new int, &IntDeleter);
// 函数对象形式
struct IntDeleter {
void operator()(int* p) { delete p; }
};
std::unique_ptr<int, IntDeleter> uptr(new int);
void FileDeleter(FILE* fp) { if(fp) fclose(fp); }
std::shared_ptr<FILE> filePtr(fopen("test.txt", "r"), FileDeleter);
C++标准库提供了与智能指针配合使用的类型转换函数:
std::shared_ptr<Derived> derived = std::make_shared<Derived>();
std::shared_ptr<Base> base = std::static_pointer_cast<Base>(derived);
std::shared_ptr<Derived> derived2 = std::dynamic_pointer_cast<Derived>(base);
std::shared_ptr<void> voidPtr = std::reinterpret_pointer_cast<void>(derived);
创建方式选择:
// 推荐
auto ptr = std::make_shared<MyClass>(args);
auto uptr = std::make_unique<MyClass>(args);
// 不推荐(可能造成内存泄漏)
std::shared_ptr<MyClass> ptr(new MyClass(args));
参数传递:
// 只读访问:传递const引用
void func(const std::shared_ptr<MyClass>& ptr);
// 需要取得所有权:按值传递
void func(std::unique_ptr<MyClass> ptr);
避免混用:
// 危险:可能导致双重删除
MyClass* raw = new MyClass();
std::shared_ptr<MyClass> p1(raw);
std::shared_ptr<MyClass> p2(raw); // 错误!
循环引用解决方案:
class Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 使用weak_ptr打破循环
};
通过掌握这些使用格式和成员函数,你可以更加安全高效地在C++程序中使用智能指针进行内存管理。记住,智能指针的目的是让资源管理更加自动化,但前提是你要正确使用它们。
小疑问:std::make_unique(10)中,如果<>中是一个自定义的类,那么()中的参数会被调用至这个类的构造函数吗?
是的,当使用
std::make_unique
创建一个自定义类的对象时,()
中的参数会被完美转发(perfect-forward)到该类的构造函数。这是std::make_unique
的一个重要特性。
对于 std::make_unique<ClassName>(args...)
:
<ClassName>
:指定要创建的对象类型(args...)
:会被转发给 ClassName
的构造函数#include <memory>
#include <iostream>
class MyClass {
public:
int value;
std::string name;
// 构造函数1
MyClass(int v, const std::string& n) : value(v), name(n) {
std::cout << "Constructed MyClass(" << value << ", " << name << ")\n";
}
// 构造函数2
explicit MyClass(int v) : value(v), name("default") {
std::cout << "Constructed MyClass(" << value << ")\n";
}
~MyClass() {
std::cout << "Destroyed MyClass(" << value << ", " << name << ")\n";
}
};
int main() {
// 调用构造函数1
auto obj1 = std::make_unique<MyClass>(42, "example");
// 调用构造函数2
auto obj2 = std::make_unique<MyClass>(100);
// 输出对象内容
std::cout << "obj1: " << obj1->value << ", " << obj1->name << "\n";
std::cout << "obj2: " << obj2->value << ", " << obj2->name << "\n";
return 0;
// obj1和obj2在这里会自动销毁
}
Constructed MyClass(42, example)
Constructed MyClass(100)
obj1: 42, example
obj2: 100, default
Destroyed MyClass(100, default)
Destroyed MyClass(42, example)
make_unique
使用完美转发,可以保持参数的值类别(左值/右值)new
,make_unique
提供了更强的异常安全保证make_unique
通常比直接使用 new
更高效new
的对比// 使用make_unique(推荐)
auto ptr1 = std::make_unique<MyClass>(42, "hello");
// 直接使用new(不推荐)
std::unique_ptr<MyClass> ptr2(new MyClass(42, "hello"));
make_unique
的优势:
new
即使是特殊构造函数也能正常工作:
class Special {
public:
Special(std::initializer_list<int> list) { /*...*/ }
Special(int, double) { /*...*/ }
};
// 使用初始化列表构造函数
auto sp1 = std::make_unique<Special>({1, 2, 3});
// 使用多参数构造函数
auto sp2 = std::make_unique<Special>(10, 3.14);
std::make_unique<ClassName>(args...)
中的 args...
确实会被转发到 ClassName
的构造函数:
unique_ptr
的推荐方式