动态内存¶
约 1803 个字 270 行代码 7 张图片 预计阅读时间 12 分钟
- 静态内存保存局部static对象,类static数据成员以及任何定义在任何函数之外的变量
- 全局对象程序启动时分配,在程序结束时销毁
动态内存与智能指针¶
-
每个程序有一个内存池,叫做自由空间或堆
-
动态对象的生存期由程序来控制
-
智能指针负责自动释放所指向的对象
- share_ptr允许多个指针指向同一个对象
- unique_ptr则“独占”所指向的对象
- weak_ptr是一种弱引用,指向shared_ptr所管理的对象
shared_ptr类¶
- 是一个模板,默认初始化的智能指针中保存着一个空指针
make_shared¶
- 最安全的分配和使用动态内存的方法
- 在动态内存中分配一个对象并初始化它;返回指向该对象的shared_ptr
shared_ptr的拷贝和赋值¶
- 进行拷贝或赋值操作时,每个shared_ptr会记录有多少个其他shared_ptr指向相同的对象
- 用一个shared_ptr初始化另一个shared_ptr,作为参数传递,作为返回值返回;所关联的计数器会递增
- 为shared_ptr赋新值,shared_ptr被销毁;计数器递减;计数器变为0,指针自动释放对象
- shared_ptr自动销毁管理的对象;自动释放相关联的内存
引用计数增加¶
- 一个shared_ptr初始化另一个shared_ptr,新的shared_ptr增加
- 参数传递
- 返回一个shared_ptr
引用计数减少¶
- 一个shared_ptr初始化另一个shared_ptr,旧的shared_ptr较少
- 被销毁
直接管理内存¶
new¶
- 动态分配const对象,必须进行初始化;返回的指针是一个指向常量的指针
- 需要显示地释放内存
share_ptr与new结合¶
- 使用一个内置指针访问一个智能指针所负责的对象是危险的
- 不要使用get初始化另一个智能指针或为智能指针赋值
C++
shared_ptr<int> p(new int(42));
int *q = p.get(); // 不要让q管理的指针被释放
{
shared_ptr<int> (q); // 两个独立的shared_ptr指向相同的内存
} // 程序块结束;q指向的内存被释放
int foo = *p; // p的内存已经被释放了
智能指针和异常¶
删除器¶
- 完成对share_ptr中保存的指针进行释放
C++
void end_connection(connection *p) { disconnect(*p); }
void f(destination &d)
{
connection c = connect(&d);
share_ptr<connection> p(&c, end_connection);
}
智能指针陷阱¶
- 不使用相同的内置指针值初始化或reset多个智能指针
- 不使用get初始化或reset另一个智能指针
- 使用get返回的指针,最后一个对应的智能指针销毁后,指针变为无效
- 使用智能指针管理的资源不是new分配的内存;传递一个删除器
unique_ptr¶
- 某个时刻智能有一个unique_ptr指向一个给定对象
- unique_ptr不支持拷贝和赋值;只指向一个对象
所有权转移¶
C++
// 所有权从p1转给p2
unique_ptr<string> p2(p1.release());
unique_ptr<string> p3(new string("Trex"));
// 所有权从p3转给p2
p2.reset(p3.release()); // reset释放了p2原来指向的内存
传递unique_ptr和返回unique_ptr¶
- 我们可以拷贝或赋值一个将要被销毁的unique_ptr
C++
unique_ptr<int> clone(int p)
{
return unique_ptr<int> (new int(p));
}
// 返回局部对象的拷贝
unique_ptr<int> clone(int p)
{
unique_ptr<int> ret(new int(p));
...
return ret;
}
向unique_ptr传递删除器¶
- 使用了函数指针类型
C++
void f(destiation &d)
{
connection c = connect(&d);
unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);
}
weak_ptr¶
-
确定shared_ptr是否会被释放掉;使用lock来检测
-
不控制对象生存期;指向一个shared_ptr管理的对象
- 初始化用shared_ptr来初始化;wp不会改变p的引用计数,wp指向的对象可能被释放掉
- 不能使用weak_ptr直接访问对象;调用lock
- lock函数检查weak_ptr指向的对象是否存在
设计优势¶
-
方便判断某一时刻
shared_ptr
中是否已经释放对象;避免多次释放 -
解决shared_ptr环形引用问题
- 解决这种状况的办法就是将两个类中的一个成员变量改为
weak_ptr
对象,因为weak_ptr
不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB
中的成员变量改为weak_ptr
对
C++
#include <memory>
#include <iostream>
class B;
class A {
public:
~A() {
std::cout << "~A" << std::endl;
}
void setPtr(std::shared_ptr<B>& b) {
bPtr_ = b;
}
private:
std::shared_ptr<B> bPtr_;
};
class B {
public:
~B() {
std::cout << "~B" << std::endl;
}
void setPtr(std::shared_ptr<A>& a) {
aPtr_ = a;
}
private:
std::shared_ptr<A> aPtr_;
};
// 解决方案
// 将B中的智能指针换成weak_ptr
int main() {
std::shared_ptr<A> aShared = std::make_shared<A>();
std::shared_ptr<B> bShared = std::make_shared<B>();
aShared->setPtr(bShared);
bShared->setPtr(aShared);
return 0;
}
动态数组¶
- 动态分配一个空数组是合法的
- 得到的是一个指针;不能使用begin()或end()
智能指针和动态数组¶
- 不能使用.或->,使用下标来访问
C++
unique_ptr<int[]> up(new int[10]);
up.release(); // 自动调用delete []
for(size_t i = 0; i != 10; ++i)
up[i] = i;
- shared_ptr不直接支持动态数组管理
- 必须自己定义删除器来操作
C++
shared_ptr<int> sp(new int[10], [](int *p){delete[] p;}); // 定义删除器
for(size_t i = 0; i != 10; ++i)
*(sp.get() + i) = i; // 使用get获取一个内置指针
allocator类¶
- 分配的内存是原始的,未构造的
- 根据给定的对象类型来确定恰当的内存大小和对齐位置
C++
auto q = p; // q指向最后构造的元素之后的位置
alloc.construct(q++); // *q is ""
alloc.construct(q++, 2, 'c'); // *q is "cc"
alloc.construct(q++, "hi!"); // *q is "hi!"
- 使用allocate返回的内存,必须用constuct构造对象;使用未构造的内存,行为是未定义的
- 只能对真正构造的元素进行destroy操作
拷贝和填充未初始化内存算法¶
C++
auto p = alloc.allocate(vi.size() * 2);
auto q = uninitialized_copy(vi.begin(), vi.end(), p);
auto q = uninitialized_fill_n(q, vi.size(), 42);
源码¶
unique_ptr¶
- 有两个参数,第一个参数是指针所指的类型;第二个参数是删除器,规定指针释放时的操作
- 禁止左值赋值和拷贝
- 定义的变量类型以及函数应该在
__uniq_ptr_impl
类中
C++
// 成员变量
__uniq_ptr_impl<_Tp, _Dp> _M_t;
// Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
// 变量类型
using pointer = typename __uniq_ptr_impl<_Tp,_Dp>::pointer;
using element_type = _Tp;
using deleter_type = _Dp;
// 成员函数
/// Return the stored pointer.
pointer get() const noexcept
{ return _M_t._M_ptr(); }
/// Return a reference to the stored deleter.
deleter_type& get_deleter() noexcept
{ return _M_t._M_deleter(); }
/// Return a reference to the stored deleter.
const deleter_type& get_deleter() const noexcept
{ return _M_t._M_deleter(); }
/// Return @c true if the stored pointer is not null.
explicit operator bool() const noexcept
{ return get() == pointer() ? false : true; }
/// Release ownership of any stored pointer.
pointer release() noexcept
{
pointer __p = get();
_M_t._M_ptr() = pointer();
return __p;
}
class __uniq_ptr_impl¶
- 存放指针和删除器的一个结构
C++
template <typename _Tp, typename _Dp>
class __uniq_ptr_impl
{
// 用来得到指针类型
template <typename _Up, typename _Ep, typename = void>
struct _Ptr
{
using type = _Up*;
};
public:
// Constructor
// 获取tuple中的元素
pointer& _M_ptr() { return std::get<0>(_M_t); }
pointer _M_ptr() const { return std::get<0>(_M_t); }
_Dp& _M_deleter() { return std::get<1>(_M_t); }
const _Dp& _M_deleter() const { return std::get<1>(_M_t); }
private:
tuple<pointer, _Dp> _M_t;
};
shared_ptr¶
- 构造函数调用的__shared_ptr的构造函数
- 该类没有重载
*
和->
运算符,从这点看shared_ptr
似乎无法实现普通指针的功能 - 该类没有析构函数
- 只有一个友元类weak_ptr
__shared_ptr&__shared_ptr_access
¶
- 有两个类成员:
_M_ptr
(由智能指针接管的普通指针)、_M_refcount
(引用计数器,类型为__shared_count
)
从构造函数看,_M_ptr
获得了接管的普通指针的值,而_M_refcount
的构造也同样需要这个值 - 重载了*和->运算符,由shared_ptr继承使用,使得智能指针最终能拥有和普通指针一样行为,尽管智能指针本质上是一个对象
从析构函数来看,里面啥也没做,说明接管的普通指针也不是在这里释放的,所以有可能是由_M_refcount来完成释放内存这个工作,下面分析__shared_count的实现 - 将weak_ptr作为友元
C++
// Define operator-> for shared_ptr<cv void>.
// __shared_ptr_access<_Tp, _Lp, false, true>
using element_type = _Tp;
element_type*
operator->() const noexcept
{
auto __ptr = static_cast<const __shared_ptr<_Tp, _Lp>*>(this)->get();
_GLIBCXX_DEBUG_PEDASSERT(__ptr != nullptr);
return __ptr;
}
// __shared_ptr
element_type* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.
__shared_count¶
- 在
__Sp_counted_ptr
中继承__M_dispose
函数和__M_destroy
函数 __shared_count
中的_Sp_counted_base<_Lp>* _M_pi
成员由__Sp_counted_ptr
来初始化
C++
// __Sp_counted_ptr
// user_count = 0;删除指针以及整个空间
virtual void
_M_dispose() noexcept
{ delete _M_ptr; }
// weak_count = 0;释放__Sp_counted_ptr对象
virtual void
_M_destroy() noexcept
{ delete this; }
// __shared_count
_M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
__Sp_counted_base&&__Sp_counted_ptr¶
- 实现计数器的增减
- 提供了释放指针和释放计数器对象的虚函数
C++
// 实现计数器的增减和对象的销毁(user_count)
// user_count and weak_count
// Called when _M_use_count drops to zero, to release the resources
// managed by *this.
virtual void
_M_dispose() noexcept = 0;
// Called when _M_weak_count drops to zero.
virtual void
_M_destroy() noexcept
{ delete this; }
template<>
inline void
_Sp_counted_base<_S_single>::_M_add_ref_copy()
{ ++_M_use_count; }
template<>
inline void
_Sp_counted_base<_S_single>::_M_release() noexcept
{
if (--_M_use_count == 0)
{
_M_dispose();
if (--_M_weak_count == 0)
_M_destroy();
}
}
template<>
inline void
_Sp_counted_base<_S_single>::_M_weak_add_ref() noexcept
{ ++_M_weak_count; }
template<>
inline void
_Sp_counted_base<_S_single>::_M_weak_release() noexcept
{
if (--_M_weak_count == 0)
_M_destroy();
}
template<>
inline long
_Sp_counted_base<_S_single>::_M_get_use_count() const noexcept
{ return _M_use_count; }
weak_ptr¶
- 有一个__weak_count和一个指针
C++
// 成员
element_type* _M_ptr; // Contained pointer.
__weak_count<_Lp> _M_refcount; // Reference counter.
__weak_count¶
- 与shared_count相似,继承父类函数进行引用计数的加减
__enable_shared_from_this¶
- 将weak_ptr转化为shared_ptr
C++
__shared_ptr<_Tp, _Lp>
shared_from_this()
{ return __shared_ptr<_Tp, _Lp>(this->_M_weak_this); }
__shared_ptr<const _Tp, _Lp>
shared_from_this() const
{ return __shared_ptr<const _Tp, _Lp>(this->_M_weak_this); }
friend const __enable_shared_from_this*
__enable_shared_from_this_base(const __shared_count<_Lp>&,
const __enable_shared_from_this* __p)
{ return __p; }
// 成员变量
mutable __weak_ptr<_Tp, _Lp> _M_weak_this;
enable_shared_from_this¶
C++
shared_ptr<_Tp>
shared_from_this()
{ return shared_ptr<_Tp>(this->_M_weak_this); }
shared_ptr<const _Tp>
shared_from_this() const
{ return shared_ptr<const _Tp>(this->_M_weak_this); }
friend const __enable_shared_from_this*
__enable_shared_from_this_base(const __shared_count<_Lp>&,
const __enable_shared_from_this* __p)
{ return __p; }
// 成员变量
mutable __weak_ptr<_Tp, _Lp> _M_weak_this;
delete this¶
- 释放管理的对象
注意事项¶
- 你必须100%绝对确保”this”对象是通过new分配的(不是通过new[],不是placement new,不是栈上的局部对象,不是全局对象,不是另一个对象的数据成员;仅仅只是通过原始的new运算符)
- 你必须100%绝对确保调用”delete this”操作的成员函数是最后调用的成员函数
- 你必须100%绝对确保在当前函数中”delete this”后,调用的其他成员函数不会读取”this”对象。
- 你必须100%确保再也不会使用”this”指针。即使你使用this指针和其他指针比较,例如nullptr,打印this指针,转换this指针等等。