面试题答案
一键面试智能指针的作用
- 管理动态分配的内存:在 C++ 中,手动管理动态分配的内存容易出现内存泄漏、悬空指针等问题。智能指针提供了一种自动管理内存的机制,在对象不再被使用时自动释放其所占用的内存。
- 简化资源管理:不仅可以管理内存资源,还可以管理其他需要在使用后释放的资源,如文件句柄、网络连接等。
智能指针的原理
- unique_ptr
- 原理:采用独占式拥有的策略,一个
unique_ptr
指针指向一块内存,并且只有它能释放这块内存。当unique_ptr
对象被销毁时(例如离开其作用域),它所指向的内存会自动被释放。unique_ptr
不允许拷贝构造和赋值操作,但支持移动语义。 - 实现方式:通常通过一个指针成员变量来指向动态分配的对象,并在析构函数中释放该对象。
- 原理:采用独占式拥有的策略,一个
- shared_ptr
- 原理:采用引用计数的策略。多个
shared_ptr
可以指向同一个对象,对象的引用计数记录了当前指向该对象的shared_ptr
的数量。当一个shared_ptr
被创建或赋值时,引用计数增加;当一个shared_ptr
被销毁(例如离开其作用域)或赋值为另一个对象时,引用计数减少。当引用计数降为 0 时,对象的内存被自动释放。 - 实现方式:除了包含一个指向对象的指针外,还包含一个指向引用计数的指针。引用计数通常是一个堆上分配的计数器对象,通过智能指针间共享这个计数器对象来实现引用计数的管理。
- 原理:采用引用计数的策略。多个
- weak_ptr
- 原理:
weak_ptr
是一种弱引用,它指向由shared_ptr
管理的对象,但不会增加对象的引用计数。它主要用于解决shared_ptr
之间的循环引用问题。weak_ptr
可以从一个shared_ptr
或另一个weak_ptr
创建。通过weak_ptr
可以获取一个shared_ptr
,但在获取之前需要检查对象是否已经被释放(通过expired()
方法)。 - 实现方式:
weak_ptr
内部通常也包含一个指向对象的指针和一个指向引用计数的指针(与shared_ptr
共享的引用计数),但它不会影响引用计数的值。
- 原理:
智能指针的使用场景
- unique_ptr
- 场景:当你希望某个对象在代码中有唯一的所有者时,使用
unique_ptr
。例如,一个函数返回一个动态分配的对象,并且调用者应该成为该对象的唯一所有者。
- 场景:当你希望某个对象在代码中有唯一的所有者时,使用
- shared_ptr
- 场景:当需要多个对象共享对某块资源的所有权时,使用
shared_ptr
。比如在实现缓存系统时,多个组件可能需要访问相同的缓存数据,这时可以用shared_ptr
来管理缓存对象。
- 场景:当需要多个对象共享对某块资源的所有权时,使用
- weak_ptr
- 场景:主要用于解决
shared_ptr
之间的循环引用问题。例如在双向链表中,节点之间的互相引用就可能导致循环引用,使用weak_ptr
可以打破这种循环。
- 场景:主要用于解决
shared_ptr 解决内存泄漏问题的方式
shared_ptr
通过引用计数机制解决内存泄漏问题。只要有 shared_ptr
指向对象,对象的引用计数就大于 0,对象不会被释放。当所有指向对象的 shared_ptr
都被销毁(引用计数降为 0)时,对象的内存会自动被释放。这避免了手动管理内存时可能出现的忘记释放内存的情况,从而有效地防止了内存泄漏。
实际代码中三者的使用方式
- unique_ptr
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed" << std::endl; }
~MyClass() { std::cout << "MyClass destructed" << std::endl; }
};
int main() {
// 创建 unique_ptr
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
// 移动 unique_ptr
std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
// 此时 ptr1 不再指向任何对象,ptr2 拥有 MyClass 对象的所有权
return 0;
}
- shared_ptr
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass() { std::cout << "MyClass constructed" << std::endl; }
~MyClass() { std::cout << "MyClass destructed" << std::endl; }
};
int main() {
// 创建 shared_ptr
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
std::shared_ptr<MyClass> ptr2 = ptr1;
// 此时 ptr1 和 ptr2 都指向同一个 MyClass 对象,引用计数为 2
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
// ptr2 离开作用域,引用计数减为 1
{
std::shared_ptr<MyClass> ptr3 = ptr1;
std::cout << "Reference count inside block: " << ptr3.use_count() << std::endl;
}
std::cout << "Reference count outside block: " << ptr1.use_count() << std::endl;
return 0;
}
- weak_ptr
#include <iostream>
#include <memory>
class MyClass;
class AnotherClass {
public:
std::weak_ptr<MyClass> weakRef;
~AnotherClass() { std::cout << "AnotherClass destructed" << std::endl; }
};
class MyClass {
public:
std::shared_ptr<AnotherClass> otherPtr;
~MyClass() { std::cout << "MyClass destructed" << std::endl; }
};
int main() {
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
std::shared_ptr<AnotherClass> ptr2 = std::make_shared<AnotherClass>();
ptr1->otherPtr = ptr2;
ptr2->weakRef = ptr1;
// 检查 weakRef 是否有效
if (!ptr2->weakRef.expired()) {
std::shared_ptr<MyClass> temp = ptr2->weakRef.lock();
std::cout << "Weak pointer lock successful" << std::endl;
} else {
std::cout << "Weak pointer expired" << std::endl;
}
return 0;
}