上一篇:《深入理解C++11》笔记-强类型枚举
本篇介绍C++11中新增的智能指针:unique_ptr、shared_ptr、weak_ptr。使用智能指针可以免于我们去主动管理内存,智能指针会自动释放内存:
class Example {
public:
Example() { std::cout << "Example()" << std::endl; }
~Example() { std::cout << "~Example()" << std::endl; }
};
int main()
{
{
std::unique_ptr<Example> unique_p1(new Example()); // Example()
} // ~Example()
{
std::shared_ptr<Example> shared_p1(new Example()); // Example()
} // ~Example()
return 0;
}
在智能指针的作用域结束时,会自动释放申请的内存。
unique_ptr
从命名上看,unique_ptr就是独有指针,这里的独有代表什么?我们来看一下例子:
std::unique_ptr<int> p1(new int(0)); // 指向int类型的智能指针
std::unique_ptr<int> p2 = p1; // 编译失败,unique_ptr不能与其他指针共享同一份内存
std::shared_ptr<int> p3 = p1; // 编译失败,unique_ptr不能与其他指针共享同一份内存
std::unique_ptr<int> p4 = std::move(p1); // 使用move函数,能够转移内存的所有权
std::shared_ptr<int> p5 = std::move(p4);
从上面的代码可以看到,unique_ptr独占了指向的内存空间,其他智能指针不能与之共享。当释放引用或离开作用域时,内存就会被释放:
class Example {
public:
Example() { std::cout << "Example()" << std::endl; }
~Example() { std::cout << "~Example()" << std::endl; }
};
int main()
{
std::unique_ptr<Example> p1(new Example()); // Example()
p1.reset(); // ~Example(),释放引用,释放内存
return 0;
}
shared_ptr
shared_ptr和unique_ptr不同,它运行多个shared_ptr共享一份内存:
std::shared_ptr<int> p1(new int(0)); // 指向int类型的智能指针
std::cout << p1.use_count() << std::endl; // 1
std::shared_ptr<int> p2 = p1; // 引用计数增加
std::cout << p1.use_count() << std::endl; // 2
shared_ptr采用了引用计数的方式来计算有几个shared_ptr拥有该内存,通过use_count成员函数能够拿到引用计数总数。当引用计数为0,或则离开作用域时,内存就会被释放:
class Example {
public:
Example() { std::cout << "Example()" << std::endl; }
~Example() { std::cout << "~Example()" << std::endl; }
};
int main()
{
std::shared_ptr<Example> p1(new Example()); // Example()
std::cout << p1.use_count() << std::endl; // 1
std::shared_ptr<Example> p2 = p1;
std::cout << p1.use_count() << std::endl; // 2
p2.reset(); // 释放引用计数
std::cout << p1.use_count() << std::endl; // 1
p1.reset(); // ~Example(),释放引用计数,计数为0,释放内存
std::cout << p1.use_count() << std::endl; // 0
return 0;
}
weak_ptr
weak_ptr不能直接指向内存空间,只能指向shared_ptr:
class Example {
public:
Example() { std::cout << "Example()" << std::endl; }
~Example() { std::cout << "~Example()" << std::endl; }
};
int main()
{
std::weak_ptr<Example> weak_p1(new Example()); // 不能直接指向内存空间
std::unique_ptr<Example> unique_p1(new Example());
std::weak_ptr<Example> weak_p2 = unique_p1; // 不能指向unique_ptr
std::shared_ptr<Example> shared_p1(new Example());
std::weak_ptr<Example> weak_p3 = shared_p1; // 不占用引用计数
std::cout << shared_p1.use_count() << std::endl; // 1
std::shared_ptr<Example> shared_p2 = shared_p1;
std::cout << weak_p3.use_count() << std::endl; // 2,同样能获取引用计数
std::shared_ptr<Example> shared_p3 = weak_p3.lock(); // lock返回shared_ptr对象
std::cout << shared_p1.use_count() << std::endl; // 3
shared_p1.reset();
shared_p2.reset();
shared_p3.reset();
if (!weak_p3.lock()) // 智能指针已经释放,weak_ptr.lock返回空指针
{
std::cout << "nullptr" << std::endl;
}
return 0;
}
weak_ptr指向shared_ptr对象,但是本身不占用引用计数;weak_ptr中的lock成员函数能够对shared_ptr智能指针进行有效性判断,防止空指针引用。
指针对象使用
现在我们了解了unique_ptr、shared_ptr、weak_ptr,再看看他们指向的对象应该怎么使用:
std::unique_ptr<int> unique_p1(new int(0));
std::cout << *unique_p1 << std::endl; // 直接使用指针对象
std::cout << *unique_p1.get() << std::endl; // 通过get获取int*
std::shared_ptr<int> shared_p1(new int(0));
std::cout << *shared_p1 << std::endl; // 直接使用指针对象
std::cout << *shared_p1.get() << std::endl; // 通过get获取int*
std::weak_ptr<int> weak_p1 = shared_p1;
std::cout << *weak_p1 << std::endl; // 不能直接使用
std::cout << *weak_p1.get() << std::endl; // 不能通过get获取int*
std::cout << *weak_p1.lock() << std::endl; // 通过lock获取shared_ptr对象,再使用对象
std::cout << *weak_p1.lock().get() << std::endl; // 通过lock获取shared_ptr对象,再使用get获取int*