大纲
C++ 智能指针深入理解
智能指针的几种类型
在 C++ 中,智能指针的类型有以下几种:
- (1) 带引用计数的智能指针:
shared_ptr
- (2) 不带引用计数的智能指针:
auto_ptr
、scoped_ptr
、unique_ptr
- (3) 特殊的智能指针:
weak_ptr
(不增加引用计数,可以用于避免 shared_ptr
发生循环引用)
智能指针 | 所有权 | 带引用计数 | 适用场景 | 核心特性 |
---|
auto_ptr | 独占(拷贝时转移) | 否 | ⚠ 已废弃,建议改用 unique_ptr | 独占所有权,在复制或赋值时会转移所有权,导致原指针变为空(nullptr ) |
scoped_ptr | 独占 | 否 | 生命周期受限于作用域,适用于简单的场景,避免资源泄漏 | 独占所有权,不可复制或赋值,不支持移动语义,即不可以使用 std::move() 函数转移所有权 |
unique_ptr | 独占 | 否 | 资源独占,生命周期明确 | 独占所有权,不可复制(拷贝构造和赋值),但可以移动(移动构造和移动赋值),即支持使用 std::move() 函数转移所有权 |
shared_ptr | 共享 | 是 | 资源共享,生命周期不固定 | 共享所有权(允许多个智能指针管理同一个资源) |
weak_ptr | 观察 shared_ptr | 否 | 避免 shared_ptr 发生循环引用 | 不增加引用计数,用于避免 shared_ptr 发生循环引用,可以通过 lock() 函数转换为 shared_ptr |
智能指针的基础使用
不带引用计数的智能指针
auto_ptr 智能指针
概念介绍
auto_ptr
是 C++ 98 引入的智能指针,具有独占所有权,在复制或赋值时会转移所有权,导致原指针变为空(nullptr
)。它可以自动释放资源,但由于容易导致空指针问题,在 C++ 11 被弃用,推荐使用 unique_ptr
或 shared_ptr
代替。auto_ptr
的核心特性如下:
独占所有权(所有权转移):
- 不能有多个
auto_ptr
共享同一资源。 - 复制或赋值时,所有权会从原指针转移到新指针,原指针会变为空(
nullptr
)。
自动释放资源:
- 在
auto_ptr
离开作用域时,会自动调用 delete
释放资源,防止内存泄漏。
已被 C++ 11 弃用:
- 由于所有权转移容易导致空指针问题,
auto_ptr
在 C++ 11 中已经被 unique_ptr
或者 shared_ptr
取代。
使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <memory>
using namespace std;
void test01() { auto_ptr<int> ptr1(new int(20)); auto_ptr<int> ptr2(ptr1); *ptr2 = 10; cout << *ptr2 << endl; }
void test02() { auto_ptr<int> ptr1(new int(20)); auto_ptr<int> ptr2(new int(30)); ptr2 = ptr1; cout << *ptr2 << endl; }
|
scoped_ptr 智能指针
概念介绍
scoped_ptr
是 Boost 库提供的一种简单智能指针,独占所有权,不可复制或赋值,不支持移动语义,即不可以使用 std::move()
函数转移所有权,在离开作用域时自动释放资源。类似于 unique_ptr
,但不支持移动语义,适用于 RAII(资源管理即初始化) 场景。在 C++ 11 之后,推荐使用 unique_ptr
代替。scoped_ptr
的核心特性如下:
独占所有权:
- 只能有一个
scoped_ptr
拥有资源,不可复制或赋值。
自动释放资源:
- 离开作用域时自动调用
delete
释放资源,防止内存泄漏。
不支持移动语义:
- 不能使用
std::move()
函数转移所有权(但相比 unique_ptr
,功能更简单)。
适用于 RAII(资源管理即初始化):
- 适合管理动态分配的对象,确保作用域结束时资源被释放。
提示
阅读 scoped_ptr
的底层源码,可以发现它的拷贝构造函数和赋值运算符重载函数都被 delete
掉,所以它才不支持复制或赋值(转移)。
使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <boost/scoped_ptr.hpp>
using namespace std; using namespace boost;
void test01() { boost::scoped_ptr<int> ptr1(new int(10)); }
void test02() { boost::scoped_ptr<int> ptr1(new int(10)); boost::scoped_ptr<int> ptr2(new int(30)); }
void test03() { boost::scoped_ptr<int> ptr1(new int(10)); }
void test04() { boost::scoped_ptr<int> ptr1(new int(10)); boost::scoped_ptr<int> ptr2(new int(30)); }
|
unique_ptr 智能指针
概念介绍
unique_ptr
是 C++ 11 引入的智能指针,独占所有权,不可复制(拷贝构造和赋值),但可以移动(移动构造和移动赋值),即支持使用 std::move()
函数转移所有权。它在离开作用域时自动释放资源,适用于 RAII(资源管理即初始化),是 auto_ptr
和 scoped_ptr
的现代替代方案。unique_ptr
的核心特性如下:
独占所有权:
- 只能有一个
unique_ptr
拥有同一资源,不可复制。
支持移动语义:
自动释放资源:
- 离开作用域时自动调用
delete
释放资源,防止内存泄漏。
支持自定义删除器:
- 可指定自定义删除器,如
std::default_delete
或 lambda。
轻量且性能优越:
- 不会有额外的引用计数(相比
shared_ptr
而言),适用于 RAII 管理单一对象。
提示
- 阅读
unique_ptr
的底层源码,可以发现它使用了带右值引用参数的拷贝构造函数和赋值运算符重载函数,所以它才可以通过 std::move()
函数转移所有权。
使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <iostream> #include <memory>
using namespace std;
void test01() { unique_ptr<int> ptr1(new int(30)); }
void test02() { unique_ptr<int> ptr1(new int(30)); unique_ptr<int> ptr2(new int(10)); }
void test03() { unique_ptr<int> ptr1(new int(10)); unique_ptr<int> ptr2(move(ptr1)); cout << *ptr2 << endl; }
void test04() { unique_ptr<int> ptr1(new int(30)); unique_ptr<int> ptr2(new int(10)); ptr2 = move(ptr1); cout << *ptr2 << endl; }
|
带引用计数的智能指针
C++ 11 带引用计数的智能指针允许多个指针共享同一个资源。当最后一个指向资源的智能指针被销毁时,资源才会被释放。它通过引用计数来跟踪有多少个智能指针指向同一资源,确保资源在不再使用时自动释放,避免内存泄漏。
- C++ 11 带引用计数的智能指针有两种,分别是:
shared_ptr
:强引用智能指针,可以改变资源的引用计数weak_ptr
:弱引用智能指针,不会改变资源的引用计数,主要用于避免 shared_ptr
智能指针的循环引用
shared_ptr 智能指针
概念介绍
shared_ptr
是 C++ 11 引入的智能指针,允许多个指针共享同一资源。它通过引用计数来跟踪有多少个指针指向同一对象,当最后一个指向该资源的 shared_ptr
被销毁时,资源会自动释放。适用于需要多个所有者共享同一资源的场景,即需要多个智能指针管理同一个资源。shared_ptr
的核心特性如下:
- 共享所有权
- 多个
shared_ptr
可以同时指向同一对象,共享资源的所有权。
- 引用计数
- 每个
shared_ptr
内部维护一个引用计数,表示当前有多少个指针引用该资源。
- 自动管理内存
使用案例
在创建 shared_ptr
智能指针时,建议使用 make_shared()
函数来创建,而不是直接 new
,其原因如下:
使用 make_shared()
函数
- 写法:
shared_ptr<Person> sp1 = make_shared<Person>();
- 只进行一次内存分配(对象数据和控制块在同一块内存中),可以减少开销。
- 资源的内存分配和
shared_ptr
创建是原子操作,可以避免内存泄漏和异常安全问题。
使用 new
关键字
- 写法:
shared_ptr<Person> sp1(new Person());
- 需要进行两次内存分配(一次是给 Person,另一次是给
shared_ptr
的控制块),性能稍低。 new
和 shared_ptr
绑定是分离的,如果 shared_ptr
还没成功创建,但 new
已经分配内存,这可能会导致内存泄漏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <iostream> #include <memory>
using namespace std;
class Person {
public: Person() { cout << "Person()" << endl; }
~Person() { cout << "~Person()" << endl; }
void show() { cout << "Using Person" << endl; }
};
int main() { shared_ptr<Person> sp1 = make_shared<Person>();
cout << "Reference count: " << sp1.use_count() << endl;
{ shared_ptr<Person> sp2 = sp1; cout << "Reference count: " << sp1.use_count() << endl; sp2->show(); }
cout << "Reference count: " << sp1.use_count() << endl;
return 0; }
|
程序运行输出的结果如下:
1 2 3 4 5 6
| Person() Reference count: 1 Reference count: 2 Using Person Reference count: 1 ~Person()
|
weak_ptr 智能指针
概念介绍
weak_ptr
是 C++ 11 的智能指针之一,主要用于避免 shared_ptr
智能指针之间的循环引用问题。它是 shared_ptr
智能指针的弱引用,不会增加引用计数,不能直接访问资源,但可以通过 lock()
函数将其转换为 shared_ptr
智能指针来访问资源。当资源已被释放时,lock()
函数会返回空指针,可用于判断对象是否仍然存在。weak_ptr
的核心特性如下:
- 不增加引用计数
- 只持有
shared_ptr
智能指针的弱引用,不影响对象生命周期。
- 防止循环引用
- 可以解决
shared_ptr
智能指针循环引用导致的内存泄漏问题。
- 检查资源状态
- 可以通过
expired()
函数判断资源是否仍然存在。
- 访问资源
- 可以通过
lock()
函数转换为 shared_ptr
智能指针,若资源已释放,则返回空指针。
- 可重置与交换
- 支持使用
reset()
函数释放关联,还支持使用 swap()
函数交换指针内容。
模拟 shared_ptr 循环引用问题
本节将演示在使用 shared_ptr
智能指针时,怎样才会发生循环引用的问题。在下述代码中,会发生智能指针循环引用的问题,最终导致堆上的对象 A 和 对象 B 无法正常析构(释放内存资源),最终导致内存泄漏,详细的分析图解请看 这里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <iostream> #include <memory>
using namespace std;
class B;
class A {
public: A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
shared_ptr<B> _ptrB;
};
class B {
public: B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
shared_ptr<A> _ptrA; };
int main() { shared_ptr<A> ptrA(new A()); shared_ptr<B> ptrB(new B());
ptrA->_ptrB = ptrB; ptrB->_ptrA = ptrA;
cout << ptrA.use_count() << endl; cout << ptrB.use_count() << endl;
return 0; }
|
程序运行输出的结果如下:
解决 shared_ptr 循环引用问题
为了解决 shared_ptr
智能指针使用不当导致的循环引用问题,可以在定义对象的时候,使用强引用智能指针(shared_ptr
);而在引用对象的时候,使用弱引用智能指针(weak_ptr
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
#include <iostream> #include <memory>
using namespace std;
class B;
class A {
public: A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
weak_ptr<B> _ptrB;
};
class B {
public: B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
weak_ptr<A> _ptrA; };
int main() { shared_ptr<A> ptrA(new A()); shared_ptr<B> ptrB(new B());
ptrA->_ptrB = ptrB; ptrB->_ptrA = ptrA;
cout << ptrA.use_count() << endl; cout << ptrB.use_count() << endl; return 0; }
|
程序运行输出的结果如下:
weak_ptr 访问对象资源
weak_ptr
智能指针是 shared_ptr
智能指针的弱引用,不会增加引用计数,不能直接访问资源,但可以通过 lock()
函数转换为 shared_ptr
智能指针来访问资源。当资源已被释放时,lock()
函数会返回空指针,可用于判断对象是否仍然存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
#include <iostream> #include <memory>
using namespace std;
class B;
class A {
public: A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void func();
weak_ptr<B> _ptrB;
};
class B {
public: B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void func();
weak_ptr<A> _ptrA; };
void A::func() { cout << "A::func()" << endl; shared_ptr<B> ptrB = _ptrB.lock(); if (ptrB != nullptr) { ptrB->func(); } }
void B::func() { cout << "B::func()" << endl; }
int main() { shared_ptr<A> ptrA(new A()); shared_ptr<B> ptrB(new B());
ptrA->_ptrB = ptrB; ptrA->func(); ptrB->_ptrA = ptrA;
cout << ptrA.use_count() << endl; cout << ptrB.use_count() << endl;
return 0; }
|
程序运行输出的结果如下:
1 2 3 4 5 6 7 8
| A() B() A::func() B::func() 1 1 ~B() ~A()
|
自定义智能指针的删除器
删除器的介绍
C++ 智能指针的删除器(Deleter)是一个可自定义的回调函数,用于在智能指针释放资源时自动执行自定义的删除操作。
unique_ptr
智能指针的删除器可以是函数指针或仿函数(函数对象),允许自定义释放方式,例如调用 delete[]
代替 delete
。shared_ptr
智能指针的删除器可以确保所有引用计数归零后执行自定义删除操作。
删除器的使用场景
在以下使用情况下,C++ 需要自定义智能指针的删除器。
- (1) 使用非
new
分配的资源。当对象的创建方式不是 new
,不能用默认 delete
释放时,需要自定义删除器。例如,使用 malloc()
分配的内存必须用 free()
释放。
1 2 3 4 5 6
| #include <memory> #include <cstdlib>
int main() { unique_ptr<int, decltype(&free)> ptr((int*)malloc(sizeof(int)), free); }
|
- (2) 管理文件、网络、数据库等系统资源。智能指针默认用
delete
释放资源,但某些系统资源(如文件、数据库连接、网络套接字)需要调用特定 API 释放资源。
1 2 3 4 5 6
| #include <memory> #include <cstdio>
int main() { unique_ptr<FILE, decltype(&fclose)> file(fopen("data.txt", "w"), fclose); }
|
- (3) 数组(
new[]
)资源的正确释放。默认的 unique_ptr
只调用 delete
,而不是 delete[]
,所以管理动态数组时,需要自定义删除器。
1 2 3 4 5 6
| #include <memory> #include <cstdio>
int main() { unique_ptr<int[], void(*)(int*)> arr(new int[10], [](int* p) { delete[] p; }); }
|
或者:
1 2 3 4 5 6
| #include <memory> #include <cstdio>
int main() { unique_ptr<int[]> arr(new int[10]); }
|
- (4) 需要额外的清理操作。对象释放时,需要额外的清理步骤,比如日志记录、计数等。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> #include <memory>
using namespace std;
void customDeleter(int* p) { cout << "Deleting pointer: " << p << endl; delete p; }
int main() { unique_ptr<int, decltype(&customDeleter)> ptr(new int(42), customDeleter); }
|
- (5) 使用
shared_ptr
时需要优化删除器大小。shared_ptr
允许自定义删除器,但它需要存储删除器对象,因此通常使用函数对象(仿函数)或 Lambda 来优化删除器大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <memory>
using namespace std;
class CustomDeleter {
public: void operator()(int* p) const { cout << "Deleting shared ptr: " << p << endl; delete p; }
};
int main() { shared_ptr<int> sp(new int(10), CustomDeleter()); }
|
或者:
1 2 3 4 5 6 7 8 9
| #include <iostream> #include <memory>
using namespace std;
int main() { auto deleter = [](int* p) { delete p; }; shared_ptr<int> sp(new int(20), deleter); }
|
删除器的使用案例
删除器的案例代码一
通过模板类单独定义了两个函数对象(仿函数),来实现 unique_ptr
智能指针的删除器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream> #include <memory>
using namespace std;
template<typename T> class ArrayDeleter {
public: void operator()(T * arrayPtr) const { cout << "Deleting array pointer" << endl; if (arrayPtr != nullptr) { delete[] arrayPtr; } } };
template<typename T> class FileDeleter {
public: void operator()(T* filePtr) const { cout << "Deleting file pointer" << endl; if (filePtr != nullptr) { fclose(filePtr); } } };
int main() { unique_ptr<int, ArrayDeleter<int>> ptr(new int[100]);
unique_ptr<FILE, FileDeleter<FILE>> ptr2(fopen("data.txt", "w"));
return 0; }
|
程序运行输出的结果如下:
1 2
| Deleting file pointer Deleting array pointer
|
删除器的案例代码二
在上述的案例代码一中,通过模板类单独定义了两个函数对象(仿函数)来实现智能指针的删除器,这略显得冗余,因为删除器在定义之后只使用了一次。为了使代码更简洁,可以使用 Lambda 表达式来定义智能指针的删除器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> #include <memory> #include <functional>
using namespace std;
int main() { unique_ptr<int, function<void(int*)>> ptr(new int[100], [](int* p) { cout << "Deleting array pointer by lambda" << endl; delete[] p; });
unique_ptr<FILE, function<void(FILE*)>> ptr2(fopen("data.txt", "w"), [](FILE* p) { cout << "Deleting file pointer by lambda" << endl; fclose(p); });
return 0; }
|
程序运行输出的结果如下:
1 2
| Deleting file pointer by lambda Deleting array pointer by lambda
|
删除器的案例代码三
除了上面介绍的删除器定义方式之外,还可以使用函数指针作为 unique_ptr
智能指针的删除器。通过 std::decltype()
来获取 arrayDeleter()
函数的类型,并将其作为 unique_ptr
智能指针的删除器类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <memory>
using namespace std;
void arrayDeleter(int* ptr) { cout << "Deleting array pointer: " << ptr << endl; delete[] ptr; }
int main() { unique_ptr<int, decltype(&arrayDeleter)> ptr(new int[10], arrayDeleter);
}
|
程序运行输出的结果如下:
1
| Deleting array pointer: 000001B05684F800
|
多线程访问共享对象的问题
在多个线程同时访问同一个对象(共享对象)时,往往会产生线程安全问题,下面将介绍如何使用 C++ 11 的 shared_ptr
与 weak_ptr
智能指针来解决线程安全问题。
thread::detach () 介绍
这里先简单介绍一下 thread::detach()
函数,有助于理解后面给出的多线程案例代码。
线程安全问题的产生
下述这段 C++ 代码会产生线程安全问题,因为主线程和子线程同时访问共享对象 Task,但是主线程在子线程访问共享对象 Task 之前,将共享对象析构掉,这会导致子线程在后续访问共享对象时产生不可预知的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <iostream> #include <thread>
using namespace std;
class Task {
public: Task() { cout << "Task()" << endl; }
~Task() { cout << "~Task()" << endl; }
void run() { cout << "invoke Task::run()" << endl; }
};
void executTask(Task *task) { this_thread::sleep_for(chrono::seconds(5)); task->run(); }
int main() { Task *task = new Task();
thread t(executTask, task);
t.detach();
delete task;
t.join();
return 0; }
|
线程安全问题的解决
这里利用 weak_ptr
智能指针可以观察 shared_ptr
智能指针状态的特性,来解决多线程访问共享对象的线程安全问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <iostream> #include <memory> #include <thread>
using namespace std;
class Task {
public: Task() { cout << "Task()" << endl; }
~Task() { cout << "~Task()" << endl; }
void run() { cout << "invoke Task::run()" << endl; }
};
void executTask(weak_ptr<Task> wp) { this_thread::sleep_for(chrono::seconds(5));
shared_ptr<Task> sp = wp.lock(); if (sp != nullptr) { sp->run(); } else { cout << "Failed to invoke Task:run()"; } }
int main() { { shared_ptr<Task> task = make_shared<Task>();
thread t(executTask, weak_ptr<Task>(task));
t.detach();
}
this_thread::sleep_for(chrono::seconds(10));
return 0; }
|
程序运行输出的结果如下:
1 2 3
| Task() ~Task() Failed to invoke Task:run()
|
智能指针的模拟实现
本节将使用 C++ 的类模板来简单模拟实现智能指针。
提示
智能指针的本质是利用栈上的对象出了作用域(比如函数作用域)后自动析构的特性,以此来实现资源的自动释放。
不带引用计数的智能指针
本节将使用类模板、右值引用、移动语义(move
)来简单模拟实现 C++ 11 中的 unique_ptr
智能指针。
扩展阅读
更多关于 C++ 11 右值引用和移动语义(move
)的介绍请看 这里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
| #include <iostream> #include <stdexcept>
using namespace std;
template<typename T> class CSmartPtr {
public: CSmartPtr(T *ptr = nullptr) : _ptr(ptr) { cout << "CSmartPtr(T *ptr)" << endl; }
~CSmartPtr() { cout << "~CSmartPtr()" << endl; delete _ptr; }
CSmartPtr(const CSmartPtr<T> &) = delete;
CSmartPtr<T> &operator=(const CSmartPtr<T> &) = delete;
CSmartPtr(CSmartPtr<T> &&src) noexcept : _ptr(src._ptr) { cout << "CSmartPtr(CSmartPtr<T> &&src)" << endl; src._ptr = nullptr; }
CSmartPtr<T> &operator=(CSmartPtr<T> &&src) noexcept { cout << "CSmartPtr<T> &operator=(CSmartPtr<T> &&src)" << endl; if (this == &src) { return *this; }
T *old_ptr = _ptr;
_ptr = src._ptr; src._ptr = nullptr;
delete old_ptr;
return *this; }
T &operator*() { if (!_ptr) { throw runtime_error("Dereferencing a null pointer!"); } return *_ptr; }
T *operator->() { if (!_ptr) { throw runtime_error("Accessing member of a null pointer!"); } return _ptr; }
T *release() noexcept { T *temp = _ptr; _ptr = nullptr; return temp; }
void reset(T *ptr = nullptr) noexcept { if (_ptr == ptr) { return; }
delete _ptr; _ptr = ptr; }
T *get() const noexcept { return _ptr; }
private: T *_ptr; };
void test01() { CSmartPtr<int> ptr1(new int(30)); }
void test02() { CSmartPtr<int> ptr1(new int(30)); CSmartPtr<int> ptr2(new int(10)); }
void test03() { CSmartPtr<int> ptr1(new int(10)); CSmartPtr<int> ptr2(move(ptr1)); cout << *ptr2 << endl; }
void test04() { CSmartPtr<int> ptr1(new int(30)); CSmartPtr<int> ptr2(new int(10)); ptr2 = move(ptr1); cout << *ptr2 << endl; }
void test05() { CSmartPtr<int> p1(new int(10)); int *rawPtr = p1.release(); delete rawPtr;
CSmartPtr<int> p2; p2.reset(new int(20)); }
int main() { test01(); test02(); test03(); test04(); test05(); }
|
C++ 11 及更高版本中的特殊成员函数行为
- 如果用户通过右值引用参数声明了移动构造函数或移动赋值运算符重载函数,而没有显式声明拷贝构造函数或赋值运算符重载函数(带左值引用参数),则:
- (1) 拷贝构造函数(带左值引用参数)会被隐式删除(
= delete
)。 - (2) 赋值运算符重载函数(带左值引用参数)会被隐式删除(
= delete
)。
是否定义了用户自定义的… | 默认拷贝构造 | 默认拷贝赋值 | 默认移动构造 | 默认移动赋值 |
---|
没有移动构造和移动赋值 | ✅ 自动生成 | ✅ 自动生成 | ❌ 不会自动生成 | ❌ 不会自动生成 |
定义了移动构造或移动赋值 | ❌ 隐式删除 | ❌ 隐式删除 | ✅ 存在 | ✅ 存在 |
带引用计数的智能指针
本节将使用类模板来简单模拟实现 C++ 11 中的 shared_ptr
智能指针,暂时不考虑线程安全问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
| #include <iostream>
using namespace std;
template<typename T> class RefCount {
public: RefCount(T *ptr = nullptr) : _ptr(ptr) { if (_ptr == nullptr) { _count = new int(0); } else { _count = new int(1); } }
~RefCount() { delete _count; }
void increDef() { ++(*_count); }
int decreRef() { if (*_count > 0) { return --(*_count); } return 0; }
int getCount() const { return *_count; }
private: T *_ptr; int *_count; };
template<typename T> class CSmartPtr {
public: CSmartPtr(T *ptr = nullptr) : _ptr(ptr) { cout << "CSmartPtr(T* ptr)" << endl; _refCount = new RefCount<T>(_ptr); }
~CSmartPtr() { cout << "~CSmartPtr()" << endl; if (0 == _refCount->decreRef()) { delete _ptr; delete _refCount; } }
CSmartPtr(const CSmartPtr<T> &src) : _ptr(src._ptr), _refCount(src._refCount) { cout << "CSmartPtr(const CSmartPtr<T>& src)" << endl; if (_ptr != nullptr) { _refCount->increDef(); } }
CSmartPtr<T> &operator=(const CSmartPtr<T> &src) { cout << "CSmartPtr<T> operator=(const CSmartPtr<T>& src)" << endl; if (this == &src) { return *this; }
if (0 == _refCount->decreRef()) { delete _ptr; delete _refCount; }
_ptr = src._ptr; _refCount = src._refCount;
_refCount->increDef();
return *this; }
T &operator*() { return *_ptr; }
T *operator->() { return _ptr; }
private: T *_ptr; RefCount<T> *_refCount; };
class Person {
public: Person(int age = 0) : _age(age) { cout << "Person(int age)" << endl; }
~Person() { cout << "~Person()" << endl; }
void print() { cout << "age: " << _age << endl; }
private: int _age; };
int main() { CSmartPtr<Person> ptr1(new Person(20));
{ CSmartPtr<Person> ptr2(ptr1); ptr2->print();
CSmartPtr<Person> ptr3; ptr3 = ptr2; ptr3->print(); }
cout << "Leaving inner scope..." << endl;
ptr1->print();
}
|
程序运行输出的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| Person(int age) CSmartPtr(T* ptr) CSmartPtr(const CSmartPtr<T>& src) age: 20 CSmartPtr(T* ptr) CSmartPtr<T> operator=(const CSmartPtr<T>& src) age: 20 ~CSmartPtr() ~CSmartPtr() Leaving inner scope... age: 20 ~CSmartPtr() ~Person()
|