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
用于其RAII的特性,使得在出现异常的情况下,动态资源能够得到释放。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;
}