stl 常用容器内存结构(持续更新)

std::string

Release

struct string{
	union Bxty{
		char Buf[0x10];
		char * Ptr;
	};
	Bxty bxty;
#ifdef _WIN64
	unsigned __int64 Mysize;
	unsigned __int64 Myres;
#else
	unsigned int Mysize;
	unsigned int Myres;
#endif
}

Mysize
字符串的长度

Myres
字符串所在内存的大小(容积)

bxty
Mysize小于0x10时,字符串放在Buf里,
Mysize大于0x10时,申请一块内存,将字符串存入,然后用Ptr指向这块内存
Debug

struct string{
    std::string::Proxy * _Myproxy
	union Bxty{
		char Buf[0x10];
		char * Ptr;
	};
	Bxty bxty;
#ifdef _WIN64
	unsigned __int64 Mysize;
	unsigned __int64 Myres;
#else
	unsigned int Mysize;
	unsigned int Myres;
#endif
}

std::vector<T>

Release

struct vector{
	T * first;
	T * last;
	T * end;
}

vector内部维护了一个顺序表
first
指向 顺序表 的第一个元素的地址
last
指向 顺序表 的最后一个元素的下一个元素的地址
end
指向这个顺序表申请的内存的末尾地址

image

Debug

struct vector{
	std::vector<T>::Proxy * _Myproxy
	T * first;
	T * last;
	T * end;
}
	struct std::vector<T>::Proxy{
		std::vector<T> * vector;
		std::vector<T>::iterator begin;
	}
std::vector<T>::iterator{
	std::vector<T>::Proxy * _Myproxy;
	std::vector<T>::iterator * begin;
	T * m_cur;
}

std::vector<T>::Proxyvector 是互相注册的关系

std::shared_ptr<T *>

std::shared_ptr{
	T * ptr;
	std::_Ref_count<T,lamdaxxx> * Rep;
}

ptr
对象的指针
Rep
引用计数对象的指针

std::_Ref_count{
	void * vtf;
	int refCount;
	int weak;
	T * ptr;
}

vtf
虚表
refCount
引用计数
当引用计数为0,调用对象的析构函数,但是Rep不一定会被销毁,因为可能有其它的weak_ptr没有析构
weak
这个标志很有意思,当refCount为0时,weak减1,当有weak_ptr指向Rep时,weak加1,当weak_ptr析构时,weak减一,当weak为0时,Rep被销毁
也就是说当没有智能指针指向对象时,Rep被销毁
ptr
对象的指针

std::list<T>

std::list<T>::node{
	node * _Next;
	node * _Prev;
	T _Myval;
}

_Next
下一个结点
_Prev
上一个结点
_Myval
保存的值,由T决定

template<class T>
class list{
	node * _Myhead;
	unsigned __int64 _Mysize; 
}

_Myhead
list维护了一个双向循环列表,_Myhead指向头结点
_Mysize
结点的个数

注意,list的头结点_Myval是没有值的

std::map

template<class FIRST,class SECOND>
class pair{
	private:
		FIRST first;
		SECOND second;
};
template<class FIRST, class SECOND, class pr =  pair<FIRST, SECOND>>
class map {

private:
	class node {
	public:
		node* lChild;
		node* father;
		node* rChild;
		byte isHeadNode;
		byte isRootNode;
		pr data;
	};
	node* head;
	unsigned __int64 size;
};

size
map节点的数量
head
指向头节点的指针,头节点的data是没有数据的,并且也不计入size,头节点的lChildfatherrChild,全部指向根节点
lChild
指向该节点的左子节点,若无的话,则指向头节点
rChild
指向该节点的右子节点,若无的话,则指向头节点
father
指向该节点的父节点
isHeadNode
是否是头节点的标记
isRootNode
是否是根节点或头节点的标记
data
存储的数据,类型为pair

std::function<T>的内存结构

  • 32位
union std__Func_Storage
{
  long double _Dummy1;
  char _Dummy2[0x24];
 void *_Ptrs[10];
};

  • 64位
union std__Func_Storage
{
  long double _Dummy1;
  char _Dummy2[0x38];
  void * _Ptrs[8];
};

function是一个非常特殊的类,下面的是的成员

  • vftable

    第一个成员是虚表,虚表的第三个虚函数为调用函数,当std::function包装的是lamda函数,调用函数就是目标lamda函数
    image

    虚表指针可能有符号,可以看出函数类型,下面给三个例子

    • lamda
    std::_Func_impl_no_alloc__main_::_2_::_lambda_1__int_int_::_vftable_
    int (*)(int)
    
    • 普通函数
    std::_Func_impl_no_alloc<int (__cdecl *)(int),int,int>::`vftable'
    int (__cdecl *)(int)
    
    • 成员函数
    const std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,void (stu::*)(int),stu *,std::_Ph<1> const &>,void,int>::`vftable'
    void (stu::*)(int)
    

  • 中间部分

    如果std::function初始化的是一个lamda,第二个成员就是lamda捕获的变量,这个成员的大小是不定的,捕获的变量总大小就是这个成员的大小

    若std::function存放的是普通函数,第二个成员就是函数地址

    若std::function存放的是成员函数,第二个成员就是函数地址,第四个成员是成员函数的this指针

  • pStorage

    第三个成员是_Ptrs数组的最后一个元素,类型为Storage *,指向本对象的起始地址

posted @ 2023-04-20 23:08  乘舟凉  阅读(140)  评论(0编辑  收藏  举报