插入映射时的内存分配
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <vector>
#include <string>
#include <iostream>
#include <map>
#include <utility>
#include <algorithm>
void * GetMemory(size_t n) {
void *ptr = malloc(n);
printf("getMem n %d ptr 0x%x\n", n, reinterpret_cast<unsigned int> (ptr));
return ptr;
}
void FreeMemory(void *p) {
free(p);
}
void* operator new (size_t n) {
void *p = GetMemory(n);
return p;
}
void* operator new [] (size_t n) {
void *p = GetMemory(n);
return p;
}
void operator delete (void *p) {
FreeMemory(p);
}
void operator delete [] (void *p) {
FreeMemory(p);
}
typedef std::vector<int> vec;
int main(int argc, char *argv[]) {
std::map<int, vec> z;
vec x;
z.insert(std::pair<int,vec>(1,x));
}
编译用 g++ -Wall -ansi test.cpp -o test
运行测试。
为什么有 3 次调用 GetMemory 并且 n = 0?
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <vector>
#include <string>
#include <iostream>
#include <map>
#include <utility>
#include <algorithm>
void * GetMemory(size_t n) {
void *ptr = malloc(n);
printf("getMem n %d ptr 0x%x\n", n, reinterpret_cast<unsigned int> (ptr));
return ptr;
}
void FreeMemory(void *p) {
free(p);
}
void* operator new (size_t n) {
void *p = GetMemory(n);
return p;
}
void* operator new [] (size_t n) {
void *p = GetMemory(n);
return p;
}
void operator delete (void *p) {
FreeMemory(p);
}
void operator delete [] (void *p) {
FreeMemory(p);
}
typedef std::vector<int> vec;
int main(int argc, char *argv[]) {
std::map<int, vec> z;
vec x;
z.insert(std::pair<int,vec>(1,x));
}
Compile with
g++ -Wall -ansi test.cpp -o test
Run test.
Why are there three calls to GetMemory with n = 0?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在 FreeMemory 中进行一些跟踪,并将 main 更改为:
}
输出:
3 个 0 大小的分配中:
这两个显然是必要的。我不确定的是:
insert
的调用中将向量复制到某处,并且在对 insert 的调用中也将其释放。就好像
insert
(或它内部调用的东西)通过值而不是通过引用获取其参数,或者insert
在某个时间之前显式地将副本放入自动变量中它分配新的地图节点。目前启动调试器对我来说很困难,我将把它留给其他人。编辑:谜团解开了。
insert
采用std::pair
,而不是std::pair
。空向量的额外副本是因为您构造的对必须转换为(另一个)临时变量,然后将该临时变量的引用传递给insert
。 std::pair 有一个构造函数模板,可以让您摆脱几乎任何事情。 20.2.2/4:我还观察到,在我的实现中,
vec x;
不会调用getMem
,但vec x(0);
会调用。实际上:是更少的代码并且剥夺了您制作额外副本的机会(尽管它改为调用
operator=
)。它仍然会进行 2 个 0 大小的分配,至少对我来说是这样。C++ 标准定义
operator[]
来返回涉及调用insert
的指定表达式的结果。我不确定这是否意味着operator[]
的效果“就像”调用了make_pair
和insert
(即标准与指定运算符[] 的源必须是什么一样好),或者只是返回的值与指定表达式产生的值相同。如果是后者,那么也许一个实现可以通过单个 0 大小的分配来完成。但是,如果不首先创建包含映射类型的对,map
就无法保证创建条目,因此应该预期会分配 2 次。或者更准确地说,是所需映射值的 2 个副本:复制 0 大小的向量会导致 0 大小的分配,这一事实取决于实现。因此,如果您遇到的情况是,该值的复制成本确实很高,但默认构造的成本却很低(例如包含大量元素的容器),那么以下内容可能会很有用:
进行 2 次大小为 4000 的分配和 2 次大小为 4000 的分配0,而:
使 3 个大小为 4000,且没有一个大小为 0。最终,大小足够大,第一个代码中的额外分配比第二个代码中的额外复制更便宜。
我不确定 C++0x 中的移动构造函数可能会对此有所帮助。
Stick some tracing in FreeMemory and change main to this:
}
Output:
So of your 3 0-sized allocations:
These two are clearly necessary. What I'm not sure about is this:
insert
, and this is also freed in the call to insert.It's as if
insert
(or something it calls internally) is taking its parameter by value instead of by reference, orinsert
is explicitly taking a copy into an automatic variable some time before it allocates the new map node. Firing up a debugger is effort for me at the moment, I'll leave it to someone else.Edit: mystery solved.
insert
takes astd::pair<const int, vec>
, not astd::pair<int, vec>
. The extra copy of an empty vector is because the pair you construct has to be converted to a(nother) temporary, then a reference to that temporary is passed toinsert
. std::pair has a constructor template that lets you get away with almost anything. 20.2.2/4:I also observe that in my implementation,
vec x;
doesn't callgetMem
, butvec x(0);
does. So actually:Is less code and denies you the opportunity to make the extra copy (although it calls
operator=
instead). It does still make 2 0-sized allocations, at least for me.The C++ standard defines
operator[]
to return the result of a specified expression involving a call toinsert
. I'm not certain whether this means the effects ofoperator[]
are "as if"make_pair
andinsert
were called (that is, the standard is as good as specifying what the source must be foroperator[]
), or just that the value returned is the same value as the specified expression would yield. If the latter then perhaps an implementation could do it with a single 0-sized allocation. But certainlymap
has no guaranteed way to create an entry without first creating a pair that contains the mapped type, so 2 allocations should be expected. Or more properly, 2 copies of the desired mapped value: the fact that copying a 0-sized vector makes a 0-sized allocation is implementation-dependent.So, if you had a case where the value was really expensive to copy, but really cheap to default-construct (like a container with lots of elements), then the following might be useful:
makes 2 allocations of size 4000 and 2 of size 0, whereas:
makes 3 of size 4000 and none of size 0. Eventually the size is big enough that the extra allocation in the first code is cheaper than the extra copying in the second code.
It's possible that move-constructors in C++0x will help with this, I'm not sure.
所有 3 种情况都与空向量的初始化有关:
All 3 cases concerned with initialization of empty vector: