C++高频面试考点 -- 智能指针
C++高频面试考点 – 智能指针
C++11中引入智能指针的概念,方便堆内存管理。这是因为使用普通指针,容易造成堆内存泄漏,二次释放,程序发生异常时内存泄漏等问题。
智能指针在C++11版本之后提供,包含在头文件<memory>
中,shared_ptr
、unique_ptr
、weak_ptr
、auto_ptr
-
shared_ptr
shared_ptr
使用引用计数、每一个shared_ptr
的拷贝都指向相同的内存。每使用它一次,内部的引用计数就加一,没析构一次,内部的引用计数就减一,减为0的时候,自动删除所指向的堆内存。shared_ptr
内部的引用计数是线程安全的,但是对象的读取需要加锁。智能指针是一个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用
make_shared
函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如:
std::shared_ptr <int> p = new int(1);
的写法是错误的。 -
unique_ptr
unique_ptr
“唯一”拥有其所指对象,也就是独享所有权语义,同一时刻只能有一个unique_ptr
指向给定对象(禁止通过拷贝语义、只有移动语义来实现)。相比于原始指针,unique_ptr
用于其RALL的特性,使得在出现异常的情况下,动态资源能够得到释放。】unique_ptr
指针本身的生命周期:从unique_ptr
指针创建时开始,直到离开作用域,离开作用域时,若其指向对象,则将其所指对象销毁。unique_ptr
指针与其所知对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。 -
weak_ptr
weak_ptr
是一种不控制对象生命周期的智能指针,它指向一个shared_ptr
管理的对象,进行该对象的内存管理的是哪个强引用的shared_ptr
。weak_ptr
设计的目的是为了配合shared_ptr
而引入的一种智能指针来协助shared_ptr
。这是因为引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要weak_ptr
来打破环形引用。如果一块内存被shared_ptr
和weak_ptr
同时引用,当所有shared_ptr
析构了之后,不管还有没有weak_ptr
引用该内存,内存也会被释放。所以weak_ptr
不保证它指向的内存一定时有效的,在使用之前使用函数lock()
检查weak_ptr
是否为空指针。 -
auto_ptr
auto_ptr
主要是为了解决“有异常抛出时发生内存泄漏”的问题。因为发生异常而无法正常释放内存。auto_ptr
不支持拷贝和赋值的操作,不能用在STL标准容器中。STL容器中的元素经常要支持拷贝、赋值的操作,在这过程中auto_ptr
会传递所有权,所以不能在STL中使用
手撕shared_ptr
#pragma once
namespace my_shared_ptr
{ template <typename T>class shared_ptr{private:/* data */T *m_data;int *m_count; //计数public:shared_ptr() : m_data(nullptr), m_count(nullptr) {}shared_ptr(T *data) : m_data(data) {if(data != nullptr) {m_count = new int(1);}}shared_ptr(const shared_ptr<T> & other) : m_data(other.m_data), m_count(other.m_count){// 拷贝构造函数if(m_data != nullptr){(*m_count) ++;}}shared_ptr(shared_ptr<T> && other) noexcept : m_data(other.m_data), m_count(other.m_count){// 移动构造函数other.m_data = nullptr;other.m_count = nullptr;}~shared_ptr(){if(m_data != nullptr) {(*m_count) --;if(*m_count <= 0){delete m_data;m_data = nullptr;delete m_count;m_count = nullptr;}}}T * get() const{return m_data;}void reset(T *data = nullptr){if(m_data == data){return;}if(m_data == nullptr) {if(data != nullptr){m_data = data;m_count = new int(1);}return;}(*m_count) --;if(*m_count <= 0) {delete m_data;m_data = nullptr;delete m_count;m_count = nullptr;}m_data = data;if(data != nullptr) {m_count = new int(1);}}int use_count() const{if(m_data == nullptr){return 0;}return *m_count;}bool unique() const{// 判断是否只有一个智能指针指向该对象if(m_data == nullptr){return false;}return *m_count == 1;}void swap(shared_ptr<T> & other){auto data = other.data;auto count = other.m_count;other.m_data = m_data;other.m_count = m_count;m_data = data;m_count = count;}T* operator -> () const{return m_data;}T& operator * () const{return *m_data;}explicit operator bool() const noexcept{return m_data != nullptr;}shared_ptr & operator = (const shared_ptr<T> & other){if(this == &other){return *this;}m_data = other.m_data;m_count = other.m_count;(*m_count)++;return *this;}shared_ptr & operator = (shared_ptr<T> && other) noexcept{if(this == &other) {return *this;}m_data = other.m_data;m_count = other.m_count;other.m_data = nullptr;other.m_count = nullptr;return *this;}};}
测试代码
#include <string>
#include <iostream>
#include "shared_ptr.h"
using namespace my_shared_ptr;class Test
{
private:std::string m_name;
public:Test(/* args */) = default;void name(const std::string & name);std::string get_name() const;~Test();
};Test::~Test()
{std::cout << "Test is deleted" << std::endl;
}void Test::name(const std::string & name)
{m_name = name;
}std::string Test::get_name() const
{return m_name;
}int main()
{auto p = new Test();shared_ptr <Test> sp(p);sp -> name("jack");std::cout << sp->get_name() << std::endl;std::cout << sp.use_count() << std::endl;shared_ptr <Test> sp2;sp2 = sp;std::cout << sp2.use_count() << std::endl;return 0;
}