每日学习记录003:(C++)unique_ptr和shared_ptr
每日学习记录003:(C++)unique_ptr和shared_ptr
在C++中,unique_ptr和shared_ptr都是智能指针,它们为动态内存管理提供了更安全、更方便的方式。
一、unique_ptr的特点
(一)独占所有权
unique_ptr拥有它所指向对象的独占所有权。这意味着在同一时间,只有一个unique_ptr可以指向特定的对象。
例如:
#include <memory>
#include <iostream>
int main() {
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
std::cout << *ptr1 << std::endl;
// 以下代码会编译错误,因为不能将一个已经被unique_ptr拥有的对象再交给另一个unique_ptr
// std::unique_ptr<int> ptr2 = ptr1;
std::unique_ptr<int> ptr2 = std::move(ptr1); // 使用std::move将ptr1的所有权转移给ptr2
std::cout << *ptr2 << std::endl;
std::cout << *ptr1 << std::endl;//这里在运行时会报错:Segmentation fault (core dumped),因为ptr1已经不再拥有对象了
return 0;
}
在这个例子中,ptr1最初拥有一个值为10的int对象。当我们想要将这个对象的所有权转移给ptr2时,不能直接赋值,而是需要使用std::move。这体现了unique_ptr的独占性。
然后注意,在move后,*ptr会报错:Segmentation fault (core dumped),因为ptr1已经不再拥有对象了,debug中也可以看到,move后ptr1就指向地址0了
(二)对象生命周期管理
当unique_ptr被销毁(例如超出作用域)时,它所指向的对象也会被自动销毁。
例如:
#include <memory>
#include <iostream>
struct MyClass {
MyClass() { std::cout << "MyClass类对象的构造函数被调用" << std::endl; }
~MyClass() { std::cout << "MyClass类对象的析构函数被调用" << std::endl; }
};
void function() {
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
}
int main() {
function();
return 0;
}
在function函数中,创建了一个MyClass类型的对象,通过unique_ptr来管理。当function函数结束时,ptr超出作用域,MyClass对象的析构函数会被自动调用。
二、shared_ptr的特点
(一)共享所有权
多个shared_ptr可以共享指向同一个对象的所有权。
例如:
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
std::shared_ptr<int> ptr2 = ptr1;
std::cout << *ptr1 << " " << *ptr2 << std::endl;
return 0;
}
这里ptr1和ptr2都指向同一个int对象,并且可以正常访问该对象的值。
(二)引用计数
shared_ptr内部使用引用计数来管理对象的生命周期。
当一个新的shared_ptr开始共享对象时,引用计数加1;当一个shared_ptr不再指向该对象(例如被重新赋值或者超出作用域)时,引用计数减1。当引用计数为0时,对象才会被销毁。
例如:
#include <memory>
#include <iostream>
struct MyClass {
MyClass() { std::cout << "MyClass类对象的构造函数被调用" << std::endl; }
~MyClass() { std::cout << "MyClass类对象的析构函数被调用" << std::endl; }
};
int main() {
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
{
std::shared_ptr<MyClass> ptr2 = ptr1;
std::cout << "Inside inner block, reference count: " << ptr1.use_count() << std::endl;
}
std::cout << "Outside inner block, reference count: " << ptr1.use_count() << std::endl;
return 0;
}
在这个例子中,在内部代码块中,ptr1和ptr2共享MyClass对象,引用计数为2。当内部代码块结束时,ptr2超出作用域,引用计数减为1。
小测验
下面的代码的打印结果是什么?
shared_ptr<Account> p1 = make_shared<Account>("Alice", 1000.0);
shared_ptr<Account> p2 = p1;
shared_ptr<Account> p3 = p2;
cout << p1.use_count() <<endl;
cout << p2.use_count() <<endl;
cout << p3.use_count() <<endl;
答案在最下面
三、unique_ptr和shared_ptr的区别
(一)所有权模式
unique_ptr是独占所有权,而shared_ptr是共享所有权。
如前面的示例所示,unique_ptr不能简单地复制来共享对象,而shared_ptr可以方便地共享。
(二)内存开销
一般情况下,unique_ptr的内存开销比shared_ptr小。
因为shared_ptr需要额外的空间来存储引用计数,而unique_ptr不需要。
(三)使用场景
当对象的所有权明确为独占时,应该使用unique_ptr,例如在函数内部创建一个对象,并且不需要在函数外部共享这个对象时。
当对象需要在多个部分之间共享时,使用shared_ptr比较合适,例如在不同的类或者模块之间共享一个对象。
小测验里,结果都是3,因为是shared_ptr 指向的是一个东西