auto_ptr
在 C++98 开始推出 auto_ptr 管理裸指针,让开发者不需要手动释放指针内存,当生命周期结束时自动释放内存。但是 auto_ptr 在转移所有权会出现野指针导致崩溃。
root@ubuntu:~/c++# cat ptr.cpp #include <iostream> #include <memory> void autoPtr() { std::auto_ptr<int> auto_ptr1(new int (1)); std::auto_ptr<int> auto_ptr2 = auto_ptr1; // 转移控制权 *auto_ptr1 += 1; // 程序崩溃 // 由于auto_ptr1转移给auto_ptr2,auto_ptr1变为空指针,导致程序崩溃,可以通过get方法先判空 if (auto_ptr1.get()) { // 判空后执行 // do somethings } } int main() { autoPtr(); return 0; }
root@ubuntu:~/c++# g++ ptr.cpp -o obj root@ubuntu:~/c++# ./obj Segmentation fault
unique_ptr
1、如何创建unique_ptr
unique_ptr 不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。
要想创建一个 unique_ptr,我们需要将一个 new 操作符返回的指针传递给unique_ptr的构造函数。
示例:
int main() {
// 创建一个unique_ptr实例
unique_ptr<int> pInt(new int(5));
cout << *pInt;
}
2、无法进行复制构造和赋值操作
unique_ptr没有 copy 构造函数,不支持普通的拷贝和赋值操作。
示例:
int main() {
// 创建一个unique_ptr实例
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2(pInt); // 报错
unique_ptr<int> pInt3 = pInt; // 报错
}
3、可以进行移动构造和移动赋值操作
unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。
如果需要转移所有权,可以使用std::move()函数。
示例:
int main() {
unique_ptr<int> pInt(new int(5));
unique_ptr<int> pInt2 = std::move(pInt); // 转移所有权
//cout << *pInt << endl; // 出错,pInt为空
cout << *pInt2 << endl;
unique_ptr<int> pInt3(std::move(pInt2));
}
4、可以返回unique_ptr
unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。
示例:
unique_ptr<int> clone(int p)
{
unique_ptr<int> pInt(new int(p));
return pInt; // 返回unique_ptr
}
int main() {
int p = 5;
unique_ptr<int> ret = clone(p);
cout << *ret << endl;
}
C++11 新出现unique_ptr
智能指针管理内存,从命名上可以知道,该智能指针智能被唯一一个对
象持有,与 auto_ptr 一样拥有唯一拥有控制权的特性,不同的是 unique_ptr 是没有复制构造函数的,只能通过显示std::move()
方法转移控制权。
include <iostream> #include <memory> void uniq_ptr() { std::unique_ptr<int> unique_ptr1(new int (1)); std::unique_ptr<int> unique_ptr2= unique_ptr1; // 转移控制权 *unique_ptr1+= 1; // 程序崩溃 } int main() { uniq_ptr(); return 0; }
int main() { // 创建一个unique_ptr实例 unique_ptr<int> pInt(new int(5)); unique_ptr<int> pInt2(pInt); // 报错 unique_ptr<int> pInt3 = pInt; // 报错 }
root@ubuntu:~/c++# g++ -std=c++11 -pthread ptr.cpp -o obj ptr.cpp: In function ‘void uniq_ptr()’: ptr.cpp:6:40: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’ std::unique_ptr<int> unique_ptr2= unique_ptr1; // 转移控制权 ^ In file included from /usr/include/c++/5/memory:81:0, from ptr.cpp:2: /usr/include/c++/5/bits/unique_ptr.h:359:7: note: declared here unique_ptr(const unique_ptr&) = delete; ^
#include <iostream> #include <memory> void uniq_ptr() { std::unique_ptr<int> unique_ptr1(new int (1)); std::unique_ptr<int> unique_ptr2= std::move(unique_ptr1); // 转移控制权 *unique_ptr1+= 1; // 程序崩溃 } int main() { uniq_ptr(); return 0; } ~
root@ubuntu:~/c++# g++ -std=c++11 -pthread ptr.cpp -o obj root@ubuntu:~/c++# ./obj Segmentation fault root@ubuntu:~/c++#
#include <memory> #include <iostream> class A { public: A() {} ~A() {} }; class B { public: B() : a(new A()) { } void output() { std::cout << "这是B对象" << std::endl; }; void test() { std::cout << "this B test " << std::endl; }; ~B() { // 不释放a std::cout << "destruction B " << std::endl; } private: A *a; }; int main(int argc, char *argv[]) { // B析构函数中A对象不会释放,导致内存泄露 B *b = new B(); b->test(); // 使用unique_ptr会自动释放内存 auto unique_ptr1 = std::unique_ptr<B>(new B()); unique_ptr1->output(); return 0; }
root@ubuntu:~/c++# g++ -std=c++11 obj.cpp -o obj root@ubuntu:~/c++# ./obj this B test 这是B对象 destruction B
只调用一次析构
shared_ptr
相对比unique_ptr
,shared_ptr
支持复制,即多个对象共享同一块内存。采用引用计数的方式式实现内存的自动管理,当复制一次值 + 1,释放一次则值 - 1,直到值为 0 内存会被回收。
#include <iostream> #include <memory> using namespace std; int main() { // 推荐方式 shared_ptr<int> sharedPtr1(new int(5)); // 匿名函数 shared_ptr<int> sharedPtr2 = make_shared<int>(5); // 工厂模式 // 引用计数 std::shared_ptr<int> sharedPtr3 = sharedPtr1; cout << sharedPtr1.use_count() << std::endl; // 输出为2 sharedPtr3.reset(); // 释放 cout << sharedPtr1.use_count() << endl; // 输出为1 return 0; }
引用计数的方式引起循环引用导致无法被正常的释放,如下示例。
#include <iostream> #include <memory> using namespace std; class Child; class Parent { public: shared_ptr<Child> child; }; class Child { public: shared_ptr<Parent> parent; }; int main() { shared_ptr<Parent> parentPtr = make_shared<Parent>(); shared_ptr<Child> childPtr = make_shared<Child>(); // 循环引用 导致内存永远不会被释放内存泄露 parentPtr->child = childPtr; childPtr->parent = parentPtr; return 0; }
,shared_ptr 也支持移动
#include <iostream> #include <memory> void share_ptr() { std::shared_ptr<int> shared_ptr1(new int (1)); std::shared_ptr<int> shared_ptr2= std::move(shared_ptr1); // 转移控制权 *shared_ptr1+= 1; // 程序崩溃 } int main() { share_ptr(); return 0; }
root@ubuntu:~/c++# g++ -std=c++11 share.cpp -o obj root@ubuntu:~/c++# ./obj Segmentation fault root@ubuntu:~/c++#
#include <iostream> #include <memory> void share_ptr() { std::shared_ptr<int> shared_ptr1(new int (1)); std::shared_ptr<int> shared_ptr2= std::move(shared_ptr1); // 转移控制权 *shared_ptr2+= 1; // 程序正常 } int main() { share_ptr(); return 0; }
root@ubuntu:~/c++# g++ -std=c++11 share.cpp -o obj root@ubuntu:~/c++# ./obj root@ubuntu:~/c++#
weak_ptr
在上一 part 中介绍得share_ptr
由于采用引用计数的方式,在循环引用时候时候 use_count 的值 + 1,导致释放后计数值依然为 1。而weak_ptr
则用于解决此问题,share_ptr
赋值给weak_ptr
,不会引起引用计数值 + 1,只是在弱引用计数值 + 1,因此不会影响到share_ptr
的生命周期。
#include <memory> #include <iostream> class A { public: A() {} ~A() {} }; class B { public: B() : a(new A()) { } void output() { std::cout << "这是B对象" << std::endl; }; void test() { std::cout << "this B test " << std::endl; }; ~B() { // 不释放a std::cout << "destruction B " << std::endl; } private: A *a; }; int main(int argc, char *argv[]) { B *b = new B(); b->test(); auto weak_ptr1 = std::weak_ptr<B>(new B()); weak_ptr1->output(); return 0; }
obj.cpp: In function ‘int main(int, char**)’: obj.cpp:37:46: error: no matching function for call to ‘std::weak_ptr<B>::weak_ptr(B*)’ auto weak_ptr1 = std::weak_ptr<B>(new B()); ^ In file included from /usr/include/c++/5/memory:82:0, from obj.cpp:1: /usr/include/c++/5/bits/shared_ptr.h:492:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp1>&&) weak_ptr(weak_ptr<_Tp1>&& __r) noexcept ^ /usr/include/c++/5/bits/shared_ptr.h:492:2: note: template argument deduction/substitution failed: obj.cpp:37:46: note: mismatched types ‘std::weak_ptr<_Tp>’ and ‘B*’ auto weak_ptr1 = std::weak_ptr<B>(new B()); ^ In file included from /usr/include/c++/5/memory:82:0, from obj.cpp:1: /usr/include/c++/5/bits/shared_ptr.h:489:7: note: candidate: std::weak_ptr<_Tp>::weak_ptr(std::weak_ptr<_Tp>&&) [with _Tp = B] weak_ptr(weak_ptr&&) noexcept = default; ^ /usr/include/c++/5/bits/shared_ptr.h:489:7: note: no known conversion for argument 1 from ‘B*’ to ‘std::weak_ptr<B>&&’ /usr/include/c++/5/bits/shared_ptr.h:486:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(const std::weak_ptr<_Tp1>&) weak_ptr(const weak_ptr<_Tp1>& __r) noexcept ^ /usr/include/c++/5/bits/shared_ptr.h:486:2: note: template argument deduction/substitution failed: obj.cpp:37:46: note: mismatched types ‘const std::weak_ptr<_Tp>’ and ‘B*’ auto weak_ptr1 = std::weak_ptr<B>(new B()); ^ In file included from /usr/include/c++/5/memory:82:0, from obj.cpp:1: /usr/include/c++/5/bits/shared_ptr.h:483:7: note: candidate: std::weak_ptr<_Tp>::weak_ptr(const std::weak_ptr<_Tp>&) [with _Tp = B] weak_ptr(const weak_ptr&) noexcept = default; ^ /usr/include/c++/5/bits/shared_ptr.h:483:7: note: no known conversion for argument 1 from ‘B*’ to ‘const std::weak_ptr<B>&’ /usr/include/c++/5/bits/shared_ptr.h:480:2: note: candidate: template<class _Tp1, class> std::weak_ptr<_Tp>::weak_ptr(const std::shared_ptr<_Tp1>&) weak_ptr(const shared_ptr<_Tp1>& __r) noexcept ^ /usr/include/c++/5/bits/shared_ptr.h:480:2: note: template argument deduction/substitution failed: obj.cpp:37:46: note: mismatched types ‘const std::shared_ptr<_Tp1>’ and ‘B*’ auto weak_ptr1 = std::weak_ptr<B>(new B()); ^ In file included from /usr/include/c++/5/memory:82:0, from obj.cpp:1: /usr/include/c++/5/bits/shared_ptr.h:477:17: note: candidate: constexpr std::weak_ptr<_Tp>::weak_ptr() [with _Tp = B] constexpr weak_ptr() noexcept = default; ^ /usr/include/c++/5/bits/shared_ptr.h:477:17: note: candidate expects 0 arguments, 1 provided
share_ptr给weak_ptr赋值
#include <memory> #include <iostream> class A { public: A() {} ~A() {} }; class B { public: B() : a(new A()) { } void output() { std::cout << "这是B对象" << std::endl; }; void test() { std::cout << "this B test " << std::endl; }; ~B() { // 不释放a std::cout << "destruction B " << std::endl; } private: A *a; }; int main(int argc, char *argv[]) { B *b = new B(); b->test(); auto weak_ptr1 = std::make_shared<B>(); weak_ptr1->output(); return 0; }
root@ubuntu:~/c++# g++ -std=c++11 obj.cpp -o obj root@ubuntu:~/c++# ./obj this B test 这是B对象 destruction B
int main(int argc, char *argv[]) { std::shared_ptr<B> share_ptr1 = std::make_shared<B>(); std::weak_ptr<B> weak_ptr1 = share_ptr1; weak_ptr1->output(); return 0; } ~
root@ubuntu:~/c++# g++ -std=c++11 obj.cpp -o obj obj.cpp: In function ‘int main(int, char**)’: obj.cpp:36:14: error: base operand of ‘->’ has non-pointer type ‘std::weak_ptr<B>’ weak_ptr1->output(); ^ root@ubuntu:~/c++#
int main(int argc, char *argv[]) { std::shared_ptr<B> share_ptr1 = std::make_shared<B>(); std::weak_ptr<B> weak_ptr1 = share_ptr1; weak_ptr1.lock()->output(); return 0; }
root@ubuntu:~/c++# ./obj
这是B对象
destruction B
root@ubuntu:~/c++#
int weak(std::shared_ptr<B> & sh) { std::weak_ptr<B> weak_ptr1 = sh; weak_ptr1.lock()->output(); return 0; } int main(int argc, char *argv[]) { std::shared_ptr<B> share_ptr1 = std::make_shared<B>(); weak(share_ptr1); return 0; }
root@ubuntu:~/c++# ./obj
这是B对象
destruction B
root@ubuntu:~/c++#
weak析构时,不调用对象的析构函数
#include <iostream> #include <memory> using namespace std; class Child; class Parent { public: shared_ptr<Child> child; }; class Child { public: weak_ptr<Parent> parent; // 改为weak_ptr不会导致强引用计数值+1 }; int main() { shared_ptr<Parent> parentPtr = make_shared<Parent>(); shared_ptr<Child> childPtr = make_shared<Child>(); // 循环引用 导致内存永远不会被释放内存泄露 parentPtr->child = childPtr; childPtr->parent = parentPtr; return 0; }
接下来,我们来看看weak_ptr的简单用法。
如何创建weak_ptr实例
当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。
示例:
int main() { shared_ptr<int> sp(new int(5)); cout << "创建前sp的引用计数:" << sp.use_count() << endl; // use_count = 1 weak_ptr<int> wp(sp); cout << "创建后sp的引用计数:" << sp.use_count() << endl; // use_count = 1 }
如何判断weak_ptr指向对象是否存在
既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,我们就不能使用weak_ptr直接访问对象。那么我们如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空shared_ptr。
————————————————
class A { public: A() : a(3) { cout << "A Constructor..." << endl; } ~A() { cout << "A Destructor..." << endl; } int a; }; int main() { shared_ptr<A> sp(new A()); weak_ptr<A> wp(sp); //sp.reset(); if (shared_ptr<A> pa = wp.lock()) { cout << pa->a << endl; } else { cout << "wp指向对象为空" << endl; } }
试试把sp.reset()这行的注释去掉看看结果有什么不同。
除此之外,weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。
示例:
class A { public: A() : a(3) { cout << "A Constructor..." << endl; } ~A() { cout << "A Destructor..." << endl; } int a; }; int main() { shared_ptr<A> sp(new A()); weak_ptr<A> wp(sp); sp.reset(); // 此时sp被销毁 cout << wp.expired() << endl; // true表示已被销毁,否则为false }
A Constructor... A Destructor... 1
如何使用weak_ptr
weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
最后,我们来看看如何使用weak_ptr来改造最前面的代码,打破循环引用问题。
class ClassB; class ClassA { public: ClassA() { cout << "ClassA Constructor..." << endl; } ~ClassA() { cout << "ClassA Destructor..." << endl; } weak_ptr<ClassB> pb; // 在A中引用B }; class ClassB { public: ClassB() { cout << "ClassB Constructor..." << endl; } ~ClassB() { cout << "ClassB Destructor..." << endl; } weak_ptr<ClassA> pa; // 在B中引用A }; int main() { shared_ptr<ClassA> spa = make_shared<ClassA>(); shared_ptr<ClassB> spb = make_shared<ClassB>(); spa->pb = spb; spb->pa = spa; // 函数结束,思考一下:spa和spb会释放资源么? }
ClassA Constructor... ClassB Constructor... ClassA Destructor... ClassB Destructor... Program ended with exit code: 0