书接上文!
C++中的指针
内存泄露问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class MyObject { public: MyObject() { cout<<"Create MyObject Object"<<'\n'; }
~MyObject() { cout<<"Delete MyObject Object"<<'\n'; } };
int main() { MyObject* obj = new MyObject(); return 0; }
|
输出:
智能指针
智能指针帮助我们自动释放不再使用的内存,从而避免内存泄漏问题。
- C++98中加入了auto_ptr
- C++11中auto_ptr更新为unique_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
| template<class T> class MySmartPtr { private: T* ptr; public: MySmartPtr(T* p = nullptr) { ptr = p; }
~MySmartPtr() { delete ptr; } };
class MyObject { public: MyObject() { cout<<"Construct MyObject"<<endl; }
~MyObject() { cout<<"Delete MyObject"<<endl; } };
int main() { MySmartPtr<MyObject> obj(new MyObject()); return 0; }
|
智能指针的声明
各种智能指针都是一个模板类。可以使用如下方法创建(以auto_ptr为例):
1 2 3
| auto_ptr<Class> valueName(new Class()); auto_ptr<vector<int>> valueName(new vector<int>()); auto_ptr<int> valueName(new int[3]);
|
auto_ptr
C++98的智能指针,已被unique_ptr替换掉,基本不用,就不介绍了。
有兴趣可以翻看这些资料:
C++智能指针:auto_ptr详解
std::auto_ptr CPP Reference
unique_ptr
auto_ptr的进阶版。
unique_ptr不共享指针,只能指向一个对象。无法复制到其他unique_ptr,无法进行值传递,也无法使用需要副本的任何STL算法。
unique_ptr的指针变动只能通过移动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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include <iostream> using namespace std;
class MyObject { public: int num; MyObject() { num = 0; cout<<"Construct MyObject"<<endl; }
MyObject(int n) { num = n; cout<<"Construct MyObject, n="<<n<<endl; }
void DebugInfo() { cout<<"DebugInfo: num = "<<num<<endl; } ~MyObject() { cout<<"Delete MyObject, n="<<num<<endl; } };
void UniquePtrAdd(unique_ptr<MyObject> obj) { obj->num += 2; }
void UniquePtrAddReference(unique_ptr<MyObject>& obj) { obj->num += 2; }
int main() { unique_ptr<MyObject> ptr1 = std::make_unique<MyObject>(); unique_ptr<MyObject> ptr2 = std::make_unique<MyObject>(4); ptr2->DebugInfo(); UniquePtrAddReference(ptr2); ptr2->DebugInfo(); cout<<"ptr1: "<<ptr1<<endl; unique_ptr<MyObject> ptr3 = std::move(ptr1); cout<<"ptr1: "<<ptr1<<endl; cout<<"ptr3: "<<ptr3<<endl; ptr3->DebugInfo(); return 0; }
|
输出:
1 2 3 4 5 6 7 8 9 10
| Construct MyObject Construct MyObject, n=4 DebugInfo: num = 4 DebugInfo: num = 6 ptr1: 00000238CFBF7A10 ptr1: 0000000000000000 ptr3: 00000238CFBF7A10 DebugInfo: num = 0 Delete MyObject, n=0 Delete MyObject, n=6
|
可以看到,经过转移的指针已经变为空指针,而转移目标获取了被转移指针指向的地址。
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
| void SharedPtrAdd(shared_ptr<MyObject> obj) { obj->num += 2; cout<<"SharedPtr Count:"<<obj.use_count()<<endl; }
void SharedPtrAddReference(shared_ptr<MyObject>& obj) { obj->num += 3; }
int main() { shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(4); cout<<"SharedPtr 1 Count:"<<ptr1.use_count()<<endl; shared_ptr<MyObject> ptr2 = ptr1; cout<<"SharedPtr 1 Count:"<<ptr1.use_count()<<endl; shared_ptr<MyObject> ptr3 = ptr2; cout<<"SharedPtr 3 Count:"<<ptr3.use_count()<<endl<<endl;
cout<<"Before Move All Pointer"<<endl; ptr1 = std::make_shared<MyObject>(0); ptr2 = ptr1; ptr3 = ptr1; cout<<"After Move All Pointer"<<endl<<endl;
SharedPtrAdd(ptr2); SharedPtrAddReference(ptr1); cout<<"SharedPtr Count:"<<ptr3.use_count()<<endl; ptr3->DebugInfo(); return 0; }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Construct MyObject, n=4 SharedPtr 1 Count:1 SharedPtr 1 Count:2 SharedPtr 3 Count:3
Before Move All Pointer Construct MyObject, n=0 Delete MyObject, n=4 After Move All Pointer
SharedPtr Count:4 SharedPtr Count:3 DebugInfo: num = 5 Delete MyObject, n=5
|
weak_ptr
weak_ptr用于辅助shared_ptr工作,不具有普通指针的功能。没有重载*和->。
weak_ptr只能从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_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
| #include <iostream> #include "Course.h" #include "Student.h"
using namespace std;
int main() { Course* c = new Course(); shared_ptr<Student> p1(new Student()); cout<<"p1 Count:"<<p1.use_count()<<endl; weak_ptr<Student> p_weak(p1); cout<<"p1 Count:"<<p1.use_count()<<endl; cout<<"p_weak Count:"<<p_weak.use_count()<<endl; shared_ptr<Student> p3(p1); cout<<"p_weak Count:"<<p_weak.use_count()<<endl;
cout<<"p1 Course:"<<p1->course<<endl; if(!p_weak.expired()) { shared_ptr<Student> p4= p_weak.lock(); p4->SetCourse(c); cout<<"p1 Course:"<<p1->course<<endl; }
shared_ptr<Student> p4 = make_shared<Student>(); p3 = p4; p1 = p4; cout<<"p_weak Count:"<<p_weak.use_count()<<endl; return 0; }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12
| Create Course Create Student p1 Count:1 p1 Count:1 p_weak Count:1 p_weak Count:2 p1 Course:0000000000000000 p1 Course:000001DF98BCAB10 Create Student Delete Student p_weak Count:0 Delete Student
|
循环引用
当两个类分别有对方类型的一个实例,在释放指针时,就会出现释放一个指针而另一指针的为空的情况。
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
| #pragma once #include <iostream>
class Student;
class Course { public: Student* student; Course(); ~Course();
void SetStudent(Student*& s); };
#pragma once #include <iostream> #include "Course.h"
class Student { public: Course* course; Student(); ~Student();
void SetCourse(Course*& c); };
#include <iostream> #include "Course.h" #include "Student.h"
using namespace std;
int main() { Course* c = new Course(); Student* s = new Student();
c->SetStudent(s); s->SetCourse(c);
delete c; cout<<"Student Has Course: "<<s->course->student<<endl; return 0; }
|
输出:
1 2 3 4
| Create Course Create Student Delete Course Student Has Course: DDDDDDDDDDDDDDDD
|
可以看到退出时学生对象中的课程指针已经被释放掉了。如果实际应用中试图在删除某一对象后通过另一对象进行访问就会发生错误。
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
| #pragma once #include <iostream>
class Student;
class Course { public: std::shared_ptr<Student> student; Course(); ~Course();
void SetStudent(std::shared_ptr<Student>& s); };
#pragma once #include <iostream> #include "Course.h"
class Student { public: std::shared_ptr<Course> course; Student(); ~Student();
void SetCourse(std::shared_ptr<Course>& c); };
#include <iostream> #include "Course.h" #include "Student.h"
using namespace std;
int main() { shared_ptr<Course> c(new Course()); shared_ptr<Student> s(new Student()); cout<<"Course Ptr:"<<c.use_count()<<endl; cout<<"Student Ptr:"<<s.use_count()<<endl; c->SetStudent(s); s->SetCourse(c); cout<<"Course Ptr:"<<c.use_count()<<endl; cout<<"Student Ptr:"<<s.use_count()<<endl; }
|
输出:
1 2 3 4 5 6
| Create Course Create Student Course Ptr:1 Student Ptr:1 Course Ptr:2 Student Ptr:2
|
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
| #pragma once #include <iostream>
class Student;
class Course { public: std::weak_ptr<Student> student; Course(); ~Course();
void SetStudent(std::shared_ptr<Student>& s); };
#pragma once #include <iostream> #include "Course.h"
class Student { public: std::shared_ptr<Course> course; Student(); ~Student();
void SetCourse(std::shared_ptr<Course>& c); };
#include <iostream> #include "Course.h" #include "Student.h"
using namespace std;
int main() { shared_ptr<Course> c(new Course()); shared_ptr<Student> s(new Student()); cout<<"Course Ptr:"<<c.use_count()<<endl; cout<<"Student Ptr:"<<s.use_count()<<endl; c->SetStudent(s); s->SetCourse(c); cout<<"Course Ptr:"<<c.use_count()<<endl; cout<<"Student Ptr:"<<s.use_count()<<endl; }
|
输出:
1 2 3 4 5 6 7 8
| Create Course Create Student Course Ptr:1 Student Ptr:1 Course Ptr:2 Student Ptr:1 Delete Student Delete Course
|
参考资料