已知面试官常问c++大型程序内存泄露怎么办,同学们通常会回答使用智能指针,智能指针可以避免使用new来初始化对象带来的delete问题,智能指针的实现思路是raii思想。
unique_ptr即独占式智能指针,一个对象无法复制到其他unique_ptr,当然也无法进行值传递给函数。
unique_ptr堆内存空间是独有的,它的引用计数为1,一旦放弃指向的资源,资源会自动回收释放。
unique_ptr构造方法
#include<memery>
using namespace std;
unique_ptr<int>p();
unique_ptr<int>p(nullptr);
unique_ptr<int>p(new int);
unique_ptr<int>p(move(q));//移动构造函数调用
值得一提的是,unique_ptr不支持拷贝构造函数。
unique_ptr成员函数
release 释放当前指针的所有权,堆空间不会被释放
swap,get等。
重载了*,=,->,[],
unique_ptr用法如上图所示,需要注意的是release和reset用法,release释放原来智能指针后用int*类型变量接替,而reset则反过来使用unique_ptr来接替int*类型,unique_ptr最好初始化一个nullptr。
值得注意的是,使用release后,智能指针将被析构不存在,而reset方法保留原有原始指针的使用值。
unique_ptr传入函数作为参数时无法通过值传递,但可以通过引用传递,另外,可以通过get方法获得裸指针传入,也可以使用move语义将智能指针交给函数管理
值得注意的是,通过右值引用传入函数后,原指针依旧存在并可用。推荐简单的值传递。
另一个值得注意的点是函数的传值,传引用,传右值引用并不构成重载
unique_ptr具体实现
只能实现简易版的,可能和原版有差异,例如析构问题
C++手把手带你实现一个智能指针 - 知乎 (zhihu.com)
以下是代码实现:
#pragma once
#ifndef _UNIQ_PTR_
#define _UNIQ_PTR_
#include<cstdio>
#include<iostream>
using namespace std;
template<typename T>
class uniq_ptr
{
public:
uniq_ptr() {}
uniq_ptr(T* rawptr) :m_rawptr(rawptr) { delete rawptr; cout << "利用原始指针创建成功" << endl; }
uniq_ptr(uniq_ptr<T>&u_ptr) = delete;
uniq_ptr<T>& operator=(unique_ptr<T>&u_ptr) = delete;
uniq_ptr(uniq_ptr<T>&&u_ptr) noexcept
{
T* rawptr = u_ptr.get();
m_rawptr = rawptr;
rawptr = nullptr;
cout << "移动构造函数创建成功" << endl;
}
unique_ptr<T>& operator=(unique_ptr<T>&&u_ptr) noexcept
{
m_rawptr = u_ptr.m_rawptr;
u_ptr.m_rawptr = nullptr;
cout << "可以复制但是原来智能指针被析构" << endl;
return *this;
}
~uniq_ptr() { m_rawptr=nullptr; }
T& operator*()
{
return *m_rawptr;
}
T* operator->()
{
return m_rawptr;
}
operator bool()
{
return m_rawptr == nullptr;
}
T* get()
{
return m_rawptr;
}
T* release()
{
T* rawptr = m_rawptr;
m_rawptr = nullptr;
return rawptr;
}
uniq_ptr<T>&reset(T* rawptr)
{
m_rawptr = rawptr;
return *this;
}
private:
T* m_rawptr;
};
#endif
创建数组对象
二. shared_ptr
共享指针即一个对象可以由多个指针托管,当所有托管指针都不再托管该对象时,对象才会被析构,共享指针可以通过reset来重置所指对象。get方法获得共享指针的原始指针。
C++11 shared_ptr智能指针(超级详细) (biancheng.net)
与unique_ptr不同的是,shared_ptr有use_count函数用来判断有多少共享指针指向该对象,unique函数判断是否只有一个共享指针指向该对象。但共享指针没有release函数。
以下是代码实现
#pragma once
#include<bits/stdc++.h>
using namespace std;
template <typename T>
class smart_ptr
{
public:
// 构造和析构
explicit smart_ptr(T* ptr ) :ptr_(ptr)
{
shared_count = nullptr;
if (ptr)
{
shared_count = new long(1);
}
}
~smart_ptr()
{
if (ptr_ && --(*shared_count) == 0) // 析构空指针不能减少引用计数
{
delete ptr_;
delete shared_count;
}
}
// 拷贝构造和拷贝赋值运算符
smart_ptr(const smart_ptr& other)
{
ptr_ = other.ptr_;
shared_count = other.shared_count; // 浅拷贝
if (ptr_) // 非空才能增加引用计数
{
(*shared_count)++;
}
}
smart_ptr& operator=(const smart_ptr& other) // 普通的拷贝赋值
{
if (&other == this) return *this;
if (ptr_ && --(*shared_count) == 0)
{
delete ptr_;
delete shared_count;
}
ptr_ = other.ptr_;
shared_count = other.shared_count;
if(ptr_) (*shared_count)++;
return *this;
}
// 移动构造和移动赋值运算符, 只需要转移资源,不需要自增
smart_ptr(smart_ptr&& other)
{
ptr_ = other.ptr_;
shared_count = other.shared_count;
other.ptr_ = nullptr;
other.shared_count = nullptr;
}
// 成员方法
T* get()
{
return ptr_;
}
T* release()
{
if (ptr_ && --(*shared_count) == 0)
{
delete ptr_;
ptr_ = nullptr;
delete shared_count;
shared_count = nullptr;
}
return ptr_;
}
void reset(T* ptr )
{
release();
ptr_ = ptr;
shared_count = ptr_ ? new long(1) : nullptr;
}
long use_count()
{
if (ptr_ && shared_count) return *shared_count;
return 0;
}
void swap(smart_ptr& p1, smart_ptr& p2)
{
using std::swap;
swap(p1, ptr_, p2.ptr_);
swap(p1.shared_count, p2.shared_count);
}
// 运算符 * -> 重载和bool转换函数
T& operator*() const
{
return *ptr_;
}
T* operator->() const
{
return ptr_;
}
explicit operator bool() const
{
return ptr_;
}
private:
T* ptr_;
long* shared_count;
};