C++ 智能指针unique_prt、shared_ptr、weak_ptr
unique_ptr(独占智能指针)
#include <iostream>
#include <memory>
class Person {
public:
Person()
: Person(0) {}
explicit Person(int age)
: m_age(age) {
std::cout << "create" << std::endl;
}
virtual ~Person() {
std::cout << "distory" << std::endl;
}
public:
void setAge(int age) noexcept {
m_age = age;
}
int getAge() const {
return m_age;
}
private:
int m_age;
};
int main() {
{
// std::unique_ptr<Person> pPersion1(new Person());
std::unique_ptr<Person> pPersion1 = std::make_unique<Person>();
// 尽量使用 std::make_unique 或 std::make_shared, 而不是直接使用new
pPersion1->setAge(20);
// std::unique_ptr<Person> pPersion2 = pPersion1; // 编译错误
std::unique_ptr<Person> pPersion3 = std::move(pPersion1);
// unique_ptr独占对象的内存,只能通过std::move转移对象内存的所有权
// 转移发生后,再对原指针操作就会运行时崩溃,如:pPersion1->setAge(30)
pPersion3->setAge(30);
// pPersion1->setAge(30); // 运行崩溃,因为对象内存的所有权已经转移给了pPersion1
// pPersion3.reset(); // 显示释放内存, 显示释放内存后,再使用会出现运行崩溃
// pPersion1.reset(); // 不会导致运行时错误
// pPersion1是代码块的局部变量,如果不调用reset,pPersion3也会在最后自动释放内存
}
std::cout << "main end" << std::endl;
return 0;
}
shared_ptr(共享智能指针)
#include <iostream>
#include <memory>
class Person {
public:
Person()
: Person(0) {}
explicit Person(int age)
: m_age(age) {
std::cout << "create" << std::endl;
}
virtual ~Person() {
std::cout << "distory" << std::endl;
}
public:
void setAge(int age) noexcept {
m_age = age;
}
int getAge() const {
return m_age;
}
private:
int m_age;
};
int main() {
{
std::shared_ptr<Person> pPersion1 = std::make_shared<Person>();
pPersion1->setAge(20);
std::cout << "pPersion1 use_count:" << pPersion1.use_count() << std::endl; // 1
std::shared_ptr<Person> pPersion2 = pPersion1;
pPersion2->setAge(30);
std::cout << "pPersion1 use_count:" << pPersion1.use_count() << std::endl; // 2
std::cout << "pPersion2 use_count:" << pPersion2.use_count() << std::endl; // 2
std::cout << "pPersion1 age:" << pPersion1->getAge() << std::endl; // 30
std::cout << "pPersion2 age:" << pPersion2->getAge() << std::endl; // 30
pPersion1.reset();
std::cout << "pPersion2 use_count:" << pPersion2.use_count() << std::endl; // 2
// shared_ptr共享对象的内存和引用计数
// 每多一个共享同处资源的shared_ptr时,计数+1;每释放一个shared_ptr时,计数-1。
// 当shared计数为0时,则证明所有指向同一处资源的shared_ptr全部都释放了,则随即释放该资源(还会释放new出来的引用计数)。
}
std::cout << "main end" << std::endl;
return 0;
}
weak_ptr(弱引用智能指针)
#include <iostream>
#include <memory>
class Person {
public:
Person()
: Person(0) {}
explicit Person(int age)
: m_age(age) {
std::cout << "create" << std::endl;
}
virtual ~Person() {
std::cout << "distory" << std::endl;
}
public:
void setAge(int age) noexcept {
m_age = age;
}
int getAge() const {
return m_age;
}
private:
int m_age;
};
int main() {
{
std::shared_ptr<Person> pPersion1 = std::make_shared<Person>();
pPersion1->setAge(20);
std::cout << "pPersion1 use_count:" << pPersion1.use_count() << std::endl; // 1
std::weak_ptr<Person> pPersion2 = pPersion1;
std::cout << "pPersion1 use_count:" << pPersion1.use_count() << std::endl; // 1
std::cout << "pPersion2 use_count:" << pPersion2.use_count() << std::endl; // 1
// weak_ptr不会影响use_count
// pPersion2->setAge(30); // 编译错误!weak_ptr没有重载* 和 -> ,无法直接当指针用
std::shared_ptr<Person> pPersion3 = pPersion2.lock(); // 可以通过weak_ptr的lock方法获得shared_ptr。
std::cout << "pPersion1 use_count:" << pPersion1.use_count() << std::endl; // 2
std::cout << "pPersion2 use_count:" << pPersion2.use_count() << std::endl; // 2
std::cout << "pPersion3 use_count:" << pPersion3.use_count() << std::endl; // 2
// lock之后use_count变为2,因为pPersion1, pPersion3共享了对象的内存
pPersion1.reset();
pPersion3.reset();
std::cout << "pPersion2 use_count:" << pPersion2.use_count() << std::endl; // 0
// distory对象的内存已经被释放
// 对象内存的释放取决于use_count,但是计数区域的释放取决于shared_ptr和weak_ptr的计数,当两者都为0时,才会释放计数区域
std::shared_ptr<Person> pPersion4 = pPersion2.lock(); // lock失败,返回nullptr
if (nullptr == pPersion4) {
std::cout << "pPersion4 nullptr" << std::endl;
}
}
std::cout << "main end" << std::endl;
return 0;
}
循环依赖导致的内存泄露、以及解决办法
#include <iostream>
#include <memory>
class Person {
public:
Person() {
std::cout << "create" << std::endl;
}
virtual ~Person() {
std::cout << "distory" << std::endl;
}
public:
void setFather(std::shared_ptr<Person>& father) noexcept {
m_father = father;
}
void setSon(std::shared_ptr<Person>& son) noexcept {
m_son = son;
}
private:
std::shared_ptr<Person> m_father;
std::shared_ptr<Person> m_son;
};
int main() {
{
std::shared_ptr<Person> father = std::make_shared<Person>();
std::shared_ptr<Person> son = std::make_shared<Person>();
father->setSon(son);
son->setFather(father);
// 循环依赖(相互引用或环引用)时,会释放不掉
}
std::cout << "main end" << std::endl;
return 0;
}
#include <iostream>
#include <memory>
class Person {
public:
Person() {
std::cout << "create" << std::endl;
}
virtual ~Person() {
std::cout << "distory" << std::endl;
}
public:
void setFather(std::shared_ptr<Person>& father) noexcept {
m_father = father;
}
void setSon(std::shared_ptr<Person>& son) noexcept {
m_son = son;
}
private:
std::weak_ptr<Person> m_father;
std::weak_ptr<Person> m_son;
// 尽管父子可以互相访问,但是彼此都是独立的个体,无论是谁都不应该拥有另一个人的所有权,因此用weak_ptr
};
int main() {
{
std::shared_ptr<Person> father = std::make_shared<Person>();
std::shared_ptr<Person> son = std::make_shared<Person>();
father->setSon(son);
son->setFather(father);
}
std::cout << "main end" << std::endl;
return 0;
}
总结
- 当你需要一个独占资源所有权(访问权+生命控制权)的指针,且不允许任何外界访问,请使用std::unique_ptr
- 当你需要一个共享资源所有权(访问权+生命控制权)的指针,请使用std::shared_ptr
- 当你需要一个能访问资源,但不控制其生命周期的指针,请使用std::weak_ptr
推荐用法
一个shared_ptr和n个weak_ptr搭配使用 而不是n个shared_ptr
因为一般模型中,最好总是被一个指针控制生命周期,然后可以被n个指针控制访问。
逻辑上,大部分模型的生命在直观上总是受某一样东西直接控制而不是多样东西共同控制。
程序上,能够完全避免生命周期互相控制引发的 循环引用问题。