为什么需要智能指针
对于普通指针,在程序结束前我们需要将每个指针都进行free
,以免造成内存泄漏。但是手动释放指针是麻烦的,并且一旦漏掉就会造成内存泄漏。因此在C++11中引入智能指针避免此种情况的发生。
智能指针包括std::shared_ptr
/std::unique_ptr
/std::weak_ptr
,需要使用头文件<memory>
。
引用计数
引用计数是为了防止内存泄漏而引入的概念。想法是基于动态分配的对象,每当增加一次对同一对象的引用,那么引用对象的引用次数就会增加1,每删除一次就减1,当计数减为0时则自动释放指向的堆内存。
std::shared_ptr
std::shared_ptr
是一种智能指针,能够记录多少个shared_ptr
共同指向同一个对象,从而消除显式地调用delete
,当计数变为0时将指针自动删除。
同样地,我们不希望显式地调用new
从而使得达成对称,因此引入std::make_shared
。
#include <iostream>
#include <memory>
void foo(std::shared_ptr<int> i) {
(*i)++;
}
int main() {
auto p = std::make_shared<int>(10);
foo(p);
std::cout << *p << std::endl; // 11
}
get()
可以获取原始指针,use_count()
可以查看指针引用计数,reset()
可以减少一个引用计数。
#include <iostream>
#include <memory>
int main() {
auto pointer = std::make_shared<int>(10);
auto pointer2 = pointer;
auto pointer3 = pointer;
int* p = pointer.get();
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
pointer2.reset();
std::cout << "pointer2.reset()" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
pointer3.reset();
std::cout << "pointer3.reset()" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
}
/*
pointer.use_count() = 3
pointer2.use_count() = 3
pointer3.use_count() = 3
pointer2.reset()
pointer.use_count() = 2
pointer2.use_count() = 0
pointer3.use_count() = 2
pointer3.reset()
pointer.use_count() = 1
pointer2.use_count() = 0
pointer3.use_count() = 0
*/
std::unique_ptr
std::unique_ptr
是一种独占的智能指针,禁止与其他智能指针共享一个对象,从而保证代码的安全。
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> pointer = std::make_unique<int>(10);
auto pointer2 = pointer;
std::cout << *pointer2 << std::endl;
}
很明显这样会报错
错误 C2280 “std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)”: 尝试引用已删除的函数 shared_pointer C:\\Users\\hasee\\Desktop\\code\\test\\shared_pointer\\test_1.cpp 6
虽然std::unique_ptr
是独占,不可转移,我们可以使用C++11中的std::move()
转移给其他std::unique_ptr
。转移并不是复制,例如p1
转移给空指针p2
后,p2
变成p1
,p1
变成空。
#include <iostream>
#include <memory>
struct Foo {
Foo() { std::cout << "Foo::Foo" << std::endl; }
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
void foo() { std::cout << "Foo::foo" << std::endl; }
};
void f(const Foo &) {
std::cout << "f(const Foo&)" << std::endl;
}
int main() {
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
if (p1) p1->foo(); // 不空
{
std::unique_ptr<Foo> p2(std::move(p1));
f(*p2);
if (p2) {
p2->foo(); // 不空
}
if (p1) {
p1->foo();
}
p1 = std::move(p2); // 空
if (p2) {
p2->foo(); // 空
}
std::cout << "p2 gg" << std::endl;
}
if (p1) {
p1->foo();
}
}
std::weak_ptr
std::shared_ptr
虽然相比普通指针优化了内存泄漏的情况,但是仍然存在内存泄漏的可能。
仔细观察可以发现std::shared_ptr
通过引用计数销毁指针是做了一个根据依赖关系类似拓扑排序的删点过程,那么也就是说如果依赖关系存在环,那么std::shared_ptr
同样无法释放资源,造成内存泄漏。
因此引入std::weak_ptr
,弱引用不会使引用计数增加,因此在析构时可以销毁。
std::weak_ptr
没有*
和->
运算符,因此不能对资源进行操作,而可以用于检查std::shared_ptr
是否存在。
std::weak_ptr
通常不单独使用,和std::shared_ptr
搭配使用,是一种辅助工具。
内容主要来自https://changkun.de/modern-cpp/zh-cn/05-pointers/
标签:std,p1,C++,智能,shared,unique,ptr,指针 From: https://www.cnblogs.com/lgats324/p/18094152