在使用普通指针的时候,需要在初始化的时候分配内存,用完之后再释放内存,如果没有及时释放内存,就会导致内存泄漏,久而久之内存满了之后程序崩溃;
智能指针就是会自动的进行内存的分配和释放,在C++中,使用智能指针需要引入头文件
#include <memory>
智能指针分为:unique_ptr,shared_ptr,weak_ptr
先看一下智能指针的简单实现
#include <iostream>
using namespace std;
class SmartPoint {
private:
int* ptr;
public:
explicit SmartPoint(int* p = NULL) { ptr = p; } //构造函数初始化分配内存,使用explicit关键字防止构造函数隐式转换
~SmartPoint() { delete ptr; } //析构时释放内存
//重载操作符
int& operator*() { return *ptr; }
};
int main() {
SmartPoint ptr(new int());
*ptr = 22;
cout << *ptr << endl; //22;
return 0;
}
即使使用之后没有手动释放,智能指针在销毁时也会调用析构函数进行释放,加上泛型之后
#include <iostream>
using namespace std;
template<class T>
class SmartPoint {
private:
T* ptr;
public:
explicit SmartPoint(T* p = NULL) { ptr = p; } //构造函数初始化分配内存,使用explicit关键字防止构造函数隐式转换
~SmartPoint() { delete ptr; } //析构时释放内存
//重载操作符
T& operator*() { return *ptr; }
T& operator->() { return ptr; }
};
int main() {
SmartPoint<int> ptr(new int());
*ptr = 22;
cout << *ptr << endl; //22;
return 0;
}
正式的讲一讲智能指针
Unique_ptr
unique_ptr是独享所有权的智能指针,不能把共享资源,即不能同时两个unique_ptr指向同一块内存;可以通过move()进行所有权的转移,转移之后,被转移所有权的指针将不能再访问资源
#include <iostream>
#include <memory>
using namespace std;
class Person {
private:
int age;
boolean sex;
public:
Person(int a, boolean s) {
age = a;
sex = s;
}
void printPerson() {
cout << "age: " << age << ", sex: " << sex << endl;
}
};
int main() {
unique_ptr<Person> p1(new Person(10, true));
p1->printPerson(); //输出 age: 10, sex: 1;
unique_ptr<Person> p2;
//p2 = p1; //无法共享,编译不通过
//使用move()转移所有权
p2 = move(p1);
p2->printPerson(); //输出 age: 10, sex: 1;
//p1->printPerson(); //p1已没有访问资源的权限,此语句运行时会抛出异常
return 0;
}
Shared_ptr
shared_ptr是共享资源的指针,可以多个指针共享同一资源,采用了引用计数的机制和weak_count(weak_ptr)管理内存,当引用计数为0且weak_count为0即释放内存,使用use_count()可以查看当前引用计数
#include <iostream>
#include <memory>
using namespace std;
class Person {
private:
int age;
boolean sex;
public:
Person(int a, boolean s) {
age = a;
sex = s;
}
void printPerson() {
cout << "age: " << age << ", sex: " << sex << endl;
}
};
int main() {
shared_ptr<Person> p1(new Person(10, 1));
p1->printPerson(); //输出 age: 10, sex: 1;
p1.use_count(); //1
shared_ptr<Person> p2;
p2 = p1;
p2->printPerson(); //输出 age: 10, sex: 1;
p1.use_count(); //2
return 0;
}
Weak_ptr
shared_ptr可以共享资源,由于采用引用计数的问题,如果出现循环引用,则会导致引用计数出现问题,不能正确的释放内存,如下
#include <iostream>
#include <memory>
using namespace std;
class Woman;
class Man {
private:
shared_ptr<Woman> woman;
public:
Man() {
cout << "Man Construction" << endl;
}
~Man() {
cout << "Man Deconstrcution" << endl;
}
};
class Woman {
private:
shared_ptr<Man> man;
public:
Woman () {
cout << "Woman Construction" << endl;
}
~Woman () {
cout << "Woman Deconstrcution" << endl;
}
};
int main() {
shared_ptr<Man> man(new Man());
shared_ptr<Woman> woman(new Woman());
man->woman = woman;
woman->man = man;
cout << man.use_count() << endl; //2
cout << woman.use_count() << endl; //2
return 0;
}
weak_ptr就是为了解决上述循环引用的问题而出现的,将shared_ptr被赋值给weak_ptr时,引用计数不会增加,weak_ptr是跟shared_ptr进行绑定的,weak_ptr可以用来跟踪shared_ptr对象,而不使引用计数改变,当引用计数为0时,shared_ptr释放资源,但不释放本身(内存),会等待weak_ptr的引用计数为0后,再释放自身。
也就是将上述代码中的一个shared_ptr改成weak_ptr即可
class Woman {
private:
weak_ptr<Man> man;
public:
Woman () {
cout << "Woman Construction" << endl;
}
~Woman () {
cout << "Woman Deconstrcution" << endl;
}
};
智能指针的创建
创建unique_ptr和shared_ptr时,有两种创建方式,一种是使用make_unique和make_shared,一种是上面的写法
make_unique
std::unique_ptr<Class> p1(new Class());
//make
auto p1(std::make_unique<Class>())
首先可以减少代码量,加快编译的速度,普通写法要写两次类名,编译器在编译时会进行两次类型推导,而使用make_unique,只写一次,即加快了编译速度;make_unique是不允许自定义析构器的
make_shared
从shared_ptr内存模型来看,一部分是资源区,一部分是用作管理区,使用普通创建方式,则会在堆上申请两次内存,第一次用作资源区,第二次用作管理区。而使用make_shared创建,则只会申请一次内存,一半用作资源区,一半用作管理区
虽然make_shared比较效率,但同样不能自定义析构器,而且shared_ptr对象会延迟释放,因为资源区和管理区是同一块内存