C++ STL 矢量模板如何将其对象存储在 Visual Studio 编译器实现中?

Posted

技术标签:

【中文标题】C++ STL 矢量模板如何将其对象存储在 Visual Studio 编译器实现中?【英文标题】:How does the C++ STL vector template store its objects in the Visual Studio compiler implementation? 【发布时间】:2009-06-30 09:08:05 【问题描述】:

我正在使用 autoexp.dat 和 DLL 扩展 Visual Studio 2003 调试器,以改进它在监视窗口中显示数据的方式。我使用 DLL 而不仅仅是基本的 autoexp.dat 功能的主要原因是我希望能够有条件地显示内容。例如我希望能够说“如果名称成员不是空字符串,则显示名称,否则显示[其他成员]”

我对 OOP 很陌生,对 STL 没有任何经验。所以我可能错过了显而易见的事情。

我无法显示向量成员,因为我不知道如何获取指向存储实际值的内存的指针。

我认为这些值存储在连续的内存块中是否正确?有什么方法可以访问指向该内存的指针?

谢谢!

[编辑:]澄清我的问题(我希望):

在调试器调用的 DLL 中,我使用了一个名为 ReadDebuggeeMemory 的函数,该函数会复制对象使用的内存。它不会复制对象指向的内存。所以我需要知道内部指针的实际地址值,以便能够在其上调用 ReadDebuggeeMemory。目前,获取向量内容的常用方法是返回垃圾,因为该内存尚未被复制。

[更新:]

我得到了垃圾,即使我正在查看正确的指针 _Myfirst,因为我正在创建向量的额外副本,而我应该使用指向向量的指针。那么问题就变成了:如何通过指向向量的指针访问指向向量内存的指针?这有意义吗?

【问题讨论】:

请原谅我很密集,但是你为什么不使用向量访问函数,比如 operator[]() ? 【参考方案1】:

标准向量中的元素被分配为一个连续的内存块。

您可以通过获取第一个元素的地址来获取指向内存的指针,可以通过以下几种方式完成:

std::vector<int> vec;
/* populate vec, e.g.: vec.resize(100); */

int* arr = vec.data();   // Method 1, C++11 and beyond.
int* arr = &vec[0];      // Method 2, the common way pre-C++11.
int* arr = &vec.front(); // Method 3, alternative to method 2.

但是,除非您需要将底层数组传递给一些旧接口,否则通常您可以直接使用向量上的运算符。

请注意,您最多只能访问返回值的vec.size() 元素。超出此范围的访问是未定义的行为(即使您认为为它保留了容量)。

如果你有一个指向向量的指针,你可以通过取消引用来做同样的事情:

std::vector<int>* vecptr;

int* arr = vecptr->data(); // Method 1, C++11 and beyond.
int* arr = &(*vecptr)[0];  // Method 2, the common way pre-C++11.
int* arr = &vec->front();  // Method 3, alternative to method 2.

但更好的是,尝试获取对它的引用。

关于您的解决方案

你想出了解决方案:

int* vMem = vec->_Myfirst;

只有在特定编译器版本的特定实现上才会起作用。这不是标准的,因此不能保证在编译器之间有效,甚至是不同版本的编译器。

如果您只在那个单一平台和编译器上进行开发,这似乎没问题,但最好是根据选择使用标准方式。

【讨论】:

Visual Studio 确实如此。但标准并未提及vector 的“连续内存”。 原始标准没有,但有更新(TC)。当前标准要求连续分配。 那么 Visual Studio 2003 使用连续分配? 是的。事实上,过去和现在的所有 C++ 实现都这样做。 如果它们都是正确的,但实际上对我没有帮助怎么办?他们没有回答我试图问的问题。 (是的,我知道问得不好可能是我的错。我尽力了......)【参考方案2】:

是的,这些值存储在内存的一个连续区域中,您可以自己获取第一个元素的地址。

但是,请注意,更改向量大小的操作(例如 push_back)可能会导致向量被重新分配,这意味着内存可能会移动,从而使您的指针无效。如果您使用迭代器,也会发生同样的情况。

vector<int> v;

v.push_back(1);

int* fred = &v[0];

for (int i=0; i<100; ++i)
  v.push_back(i);

assert(fred == &v[0]); // this assert test MAY fail

【讨论】:

确实,适用于向量的标准迭代器失效规则。【参考方案3】:

一旦我从具有向量变量更改为具有指向向量的指针,我就无法实施Gman 和Roddy 建议的解决方案,很可能是因为我太无能了。

我找到了我正在寻找的指针:_Myfirst

所以我的代码在我使用时可以工作

std::vector<int>* vec;

int *vMem = vec->_Myfirst;

我很惊讶我可以访问 _Myfirst。我本来希望它是一个私人成员。但显然不是……

【讨论】:

不要这样做!应用程序程序员不打算使用以下划线和大写字母开头的名称。我不知道为什么需要动态分配向量,但是要获取其第一个元素的地址 &(vec->operator[](0)) 或 &(vec->front()) 应该可以完成这项工作。 【参考方案4】:

您无法实施 Gman 和 Roddy 建议的解决方案,因为他们的回答看似正确,但与您的情况无关。

在开发用于表达式评估 (EE) 的插件 Dll 以与 autoexp.dat 一起使用时,您会从进程中获得一个指向向量对象的原始指针。但是Dll是在Visual Studio Debugger的进程中运行的,所以只能通过ReadDebuggeeMemory函数来访问vector的数据。

您应该使用该原始指针和 ReadDebuggeeMemory() 将 Debugee 进程中的对象占用的内存读取到本地缓冲区/对象。 (本地用于调试器进程,即您的 dll)。然后从本地对象获取必要的数据,如果它是一个指针,那么你必须再次使用 ReadDebuggeeMemory() 来读取它指向的对象到另一个本地位置。以此类推。

对于矢量(我自己没有做更具体的),你应该 1. 读取(ReadDebuggeeMemory())向量对象到本地。 2.获取向量的大小(我假设它是向量类中的非指针数据) 3. 获取指向该连续内存位置的指针,并将该块读取 (ReadDebuggeeMemory()) 到本地缓冲区/块。块大小为 sizeof(VectorType) * VectorSize(以字节为单位)。 (如果不是连续的,那么事情会更复杂,但思路是一样的)。 4. 由于您的向量包含指针,您必须将 (ReadDebuggeeMemory()) 每个项目分别读取到本地内存中。

【讨论】:

以上是关于C++ STL 矢量模板如何将其对象存储在 Visual Studio 编译器实现中?的主要内容,如果未能解决你的问题,请参考以下文章

C++标准模板库(STL)——vector常见用法详解

将一种类型的 STL 对象存储到另一种类型的有效方法

c++ 容器含义

C++ :1STL 的容器概述array容器详解迭代器初步分析

[C++][转载]std::map用法

C++ STL 相关 部分笔记