Class中的容器问题
来源:互联网 发布:淘宝直播怎么申请的 编辑:程序博客网 时间:2024/05/16 09:27
前些天工作中同事突然为我一句:class 中vector到底占用多大的空间?它占用的空间会不会随着push_back添加元素而产生越界呢??当时听到这个问题是一头雾水,甚至有种被对方的分析说服。作为一个好奇心极强的程序员,我还是打开了VS探究了这其中的奥秘,现在就以大家最为熟悉的vector为例,讲讲vector到底是如何管理内存和如何在class 中被存储的。
为了解决以上的疑惑,我们先看vector的数据定义和内存分配策略,最后以一个实例来验证我们的猜想。
- vector 的数据结构定义:
template <class T, class Alloc = alloc> class vector {...protected: iterator start; //表示目前已使用空间的头 iterator finish; //表示目前已使用空间的尾iterator end_of_storage; //表示目前可用空间的尾(已分配)...public: iterator begin() { return start; } const_iterator begin() const { return start; } iterator end() { return finish; } const_iterator end() const { return finish; } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }//求取已使用容量 size_type size() const { return size_type(end() - begin()); } size_type max_size() const { return size_type(-1) / sizeof(T); } //求取目前可使用空间容量 size_type capacity() const { return size_type(end_of_storage - begin()); } bool empty() const { return begin() == end(); }...}
由以上的数据结构定义可以看出vector的数据存储区主要是start,finish和end_of_storage这三个iterator构成,而一个iterator的大小刚好是一个指针的大小,所以vector 的对象大小就有可能是3个指针了,为例验证自己的想法,写了以下的一个简答的测试程序:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <vector>#include <list>using namespace std;class Myclass{public:vector<int>m_vector;};int _tmain(int argc, _TCHAR* argv[]){Myclass instance;int num0 = sizeof(instance); //会是多少呢instance.m_vector.push_back(1);instance.m_vector.push_back(2);int num1 = sizeof(instance);//加入元素后是不是class的对象会改变size 呢?return 0;}
- 从上面的结果中可以看到vector 的大小变化,如下图所示:
由此可以知道的是vector的push_back 操作并不改变其大小,但是这也让我有了两个疑问:
1.vector 添加元素操作并没有改变类大小的话,那么添加的元素说明也就不在class的数据区了,那么它放在哪里了呢?
2.通过数据结构知道vector 中只有三个指针,为什么它的size不是12?
首先回答为什么size是16的问题,为此我们可以打开vector查看其中存储的数据:
从std::Vector_val中可以看出vector成员中的确包含上文提到的三个指针,并且从地址上看,它们是连续且属于class的数据区(可以从class对象的首地址看出),只是其中多了一个std::Container_base12成员变量,通过查询其地址发现其与_Myfirst地址是相差8位,所以知道std::Container_base12成员变量也在class中,并且是一个容器指针(所有的STL容器都有),因此也就好理解为什么是16了。
那么vector的元素数据是放在那里的呢,它又是如何实现数据的动态添加和管理的?为此我们可以看看vector的push_back时,计算机到底做了些什么:
void push_back(const T& x) { if (finish != end_of_storage) { // 还有备用空间 construct(finish, x); // 直接在备用空间中构建元素 ++finish; // 调整范围 } else // 已无备用空间 insert_aux(end(), x); }template <class T, class Alloc>void vector<T, Alloc>::insert_aux(iterator position, const T& x) { if (finish != end_of_storage) { // 还有备用空间 // 在备用空间起始处构造一个元素,并以vector最后一个元素值为其初值 construct(finish, *(finish - 1)); ++finish; // 以下做啥用? T x_copy = x; copy_backward(position, finish - 2, finish - 1); *position = x_copy; } else {// 已无用空间 const size_type old_size = size();//此时的size()等同于capacity() const size_type len = old_size != 0 ? 2 * old_size : 1; // 以上配置原则:如果原大小为0,那么则配置1(个元素大小) // 如果原大小不为0,那么配置原大小的2倍 // 前半段用来放置原数据,后半段用来放置新数据 iterator new_start = data_allocator::allocate(len); // 实际配置 iterator new_finish = new_start; __STL_TRY { // 复制 new_finish = uninitialized_copy(start, position, new_start); construct(new_finish, x); ++new_finish; // 将安插点的原内容页拷贝过来(该函数也可能被insert(p,x)调用) new_finish = uninitialized_copy(position, finish, new_finish); } catch(...) { // "commit or rollback"(如果不成功那么一个都不留) destroy(new_start, new_finish); data_allocator::deallocate(new_start, len); throw; } // 解构并释放原vector destroy(begin(), end()); deallocate(); // 调整迭代器,指向新vector start = new_start; finish = new_finish; end_of_storage = new_start + len; }}有以上代码很容容易看到,vector在添加元素时,依据添加元素的大小动态的在堆上分配内存,并且在其生命周期结束时又释放掉 分配的空间。因此通过 allocate 分配出来的地址空间并不和class中数据区空间(上文为栈)在同一位置,而是在堆中分配的地址。因此也就实现了数据在class中大小不变,只需要保存动态内存首地址存储就可,动态变化空间在堆中被动态分配与管理。
例子:
#include "stdafx.h"#include <vector>#include <list>#include <string>#include <map>using namespace std;class Myclass{public:vector<int>m_vector;int m_age;string m_name;map<int, int>m_map;};int _tmain(int argc, _TCHAR* argv[]){Myclass instance;int num0 = sizeof(instance);instance.m_vector.push_back(1);instance.m_vector.push_back(2);int num1 = sizeof(instance);//instance.m_list.push_back(10);return 0;}通过断点调试,查看Myclass对象instance 中每一个元素的首地址知道:它们在数据区中是连续存储的;同时,通过查看vector中俩个int 类型的地址知,它们显然和class中其他成员变量的地址是不同的,依此知道容器中的元素是另外在堆中分配的,并且连续存储(下图中6fe888和6fe88c可以看出).这样也就不难解释为什么vector中添加元素不会引起class数据区的越界等问题了.
总结:
1.class的大小是其数据区元素的大小。
2.class中的数据成员是连续存储的。
3.容器变量在class中只是保留了三个指针和一个container,其元素的值是在堆中例外分配与管理的。
4.vector的大小是4个指针大小.。
- Class中的容器问题
- Bootstrap中的两个容器class
- docker容器中的权限问题
- jsp:useBean中的class问题
- 有关于java中的一些容器问题
- STL容器中的vector初始化问题
- Web Application 中的Class not found 问题
- web容器读取配置文件过程中的一个问题
- web容器读取配置文件过程中的一个问题
- Class问题
- jbuilder中的Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");问题
- Hibernate中的session.load(class,id)参数问题
- java 中的 class path, package, jar 等基本问题
- Eclipse中的Class File Editor source not found问题
- css样式中的class,style与id的优先级问题
- 修改html容器的class属性
- 使用 MAP CLASS 库创建关联容器
- JPA+Spring下,applicationContext.xml中的entityManagerFactory中的class设置问题
- c++实验7--最大公约和和最小公倍数
- Thread内存泄漏问题
- ios::ate和ios::app在C++文件中的区别
- 给一个整数N,N可以拆分出不同的因子,而且满足因子的乘积等于N。则在这些因子中和是最小的一组是什么
- 二分搜索模板
- Class中的容器问题
- HttpUtils解析XMl
- git发布简单项目
- C++走向远洋——59(项目三、图形面积、抽象类)
- HashTable vs HashMap
- 浏览器标签管理插件
- 分不清正反斜杠??一句话你就分清了
- HttpUtils 的post请求
- HttpURLConnection用法详解