返回的本地地图数据'Lucky'有效的指针数据?
'Lucky' valid pointer data to returned local map data?
在我的c++程序中,我有一个函数,它返回一个包含元素的映射,每个元素都可以有指向映射中另一个元素的指针。我在函数结束时返回映射之前设置了这些指针。示例代码:
#include <iostream>
#include <map>
#include <string>
class TestObject{
public:
TestObject(std::string message) : message(message), other(nullptr){}
TestObject* other;
std::string message;
};
std::map<std::string, TestObject> mapReturningFunction(){
std::map<std::string, TestObject> returnMap;
TestObject firstObject("I'm the first message!");
returnMap.insert(std::make_pair("first", firstObject));
TestObject secondObject("I'm the second message!");
returnMap.insert(std::make_pair("second", secondObject));
TestObject* secondObjectPointer = &(returnMap.at("second"));
returnMap.at("first").other = secondObjectPointer;
return returnMap;
}
int main(){
std::map<std::string, TestObject> returnedMap = mapReturningFunction();
std::cout << returnedMap.at("first").other->message << std::endl; // Gives a valid message every time
std::cin.get();
return 0;
}
在函数的调用点,指针other
仍然有效,尽管我怀疑它会因为函数内部的映射("指向对象"是其元素)超出作用域而失效。
这与在局部变量的内存可以在其作用域外访问中提到的基本相同吗?? 我基本上是"幸运"的指针仍然指向有效的数据?或者发生了什么不同的事情?
我真的认为它每次都是"幸运的"命中,但一些确认将是非常好的。
是的,你很"幸运"(不是很幸运,你的程序迟早会崩溃或以某种方式做不好的事情)。
:
returnMap
被分配在堆栈上:当mapReturningFunction
返回时,它将被销毁。
在映射中获取对象的地址,并将其赋值为other
指针。
当你的函数返回时,returnMap
被复制到返回值,所以(复制的)指针现在真的指向垃圾。
优化器经常避免最后一次复制,它被称为"复制省略"(或"返回值优化"),可能是您的"正常"行为的原因。但是没关系,你的程序有未定义的行为。
这不是运气。你正在返回一个指向堆栈上某个位置的指针。该位置是有效的,并且它恰好具有最后放置在那里的值,直到其他内容更改它。
这里有一个例子,我借用了你链接的另一个问题的想法,这是完全相同的:
#include <stdio.h>
int* foo()
{
int a = 5;
return &a;
}
void nukestack()
{
int a = 7;
printf("putting 7 on the stackn");
}
void main()
{
int* p = foo();
printf("%dn", *p);
nukestack();
printf("%dn", *p);
}
程序将打印如下:
5
putting 7 on the stack
7
原因如下。我们首先调用foo(),它在堆栈上为变量a分配空间。我们将5写入这个位置,然后从函数返回,释放堆栈空间,但内存保持不变。然后调用nukestack(),它在堆栈上为自己的变量a分配空间。由于两个函数非常相似,并且两个函数中的变量大小相同,因此它们的内存位置恰好重叠。
此时,新的a变量仍然具有旧的值。但是我们现在用7覆盖了5。当从函数返回时,原来的指针p仍然指向同一个位置,这里现在有一个7。
这是未定义的行为,如果你依赖它,从技术上讲你违反了规则。对于大多数编译器,当您返回指向局部变量的指针时,您还会得到一个警告,并且警告确实不应该被忽略。
是的,只有"幸运"。通过保留指向已被销毁的映射元素的指针,您将获得未定义的行为。这与保持一个指向局部映射本身的指针并不完全相同,因为映射的元素是动态分配的,但在这里得到的结果是相同的。
在这里强制执行崩溃或其他"错误"行为并不那么容易。这可能是由于返回值优化,即映射的副本被省略,因此源不会被覆盖。
但以下工作对我在vc++ 2013:
在mapReturnFunction
中,将返回语句更改为
return true ? returnMap : returnMap;
虽然这可能很奇怪,但这个技巧将禁用返回值优化,导致实际复制。用/EHsc /Za
编译(尽管这两个标志在这里没有什么区别),在我的机器上的结果是没有打印。
事实上,可观察到的行为通过一个应该没有什么区别的语句发生了如此剧烈的变化,这给了你一个强烈的暗示,表明某些事情是非常错误的。
你运气不好。复制省略或移动构造函数(c++ 11)将防止原始数据的破坏。虽然第一个是可选的编译器优化,但第二个将(如果第一个不适用)创建原始版本的移动版本,而不会使引用无效。
请注意,一旦你创建了一个副本(没有移动),并且原始元素被销毁,指向其他元素的指针就无效了!
Pre c++ 11 ,你是"幸运"。代码可能会工作,因为复制省略,特别是NRVO(命名返回值优化)意味着returnMap
和returnedMap
实际上是同一个对象。但是,您不允许信任它,因此代码调用未定义行为。
Post c++ 11,你是"幸运的",但不那么幸运。映射有一个移动构造函数,return returnMap;
隐式地将本地returnMap
移动为prvalue,然后可用于returnedMap
的移动构造函数。编译器仍然可以忽略这一点,但您可以依赖于移动的局部值。然而,该标准并没有保证容器移动时究竟发生了什么,因此您仍然调用未定义行为,但是一旦LWG开放问题2321得到解决,情况可能会发生变化。
- 有哪些有效的方法可以消除一组 100 万个字符串>重复数据?
- 一种有效的数据结构,用于按 ID 访问和查找加权随机项
- 在C++事务之间存储大量字符数据的有效方法
- 有效地将数据加载到 std::vector 中<char>
- 用于随机数据访问的最有效文件类型
- 错误 - 自定义数据类型作为有效负载,带有提升::几何
- 一种将 Dart 中的字节数据转换为 C++ 中的无符号字符*的有效方法?
- C++,在对象内分配多个数据时,堆栈分配是否更有效? 在下面的程序中,类A_Heap的效率会更低吗?
- 如何有效地实现将向量的数据分配给多个变量?
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 有没有办法有效地更新QML中的CAN数据?
- 张量(多维数组)是树前瞻数据的有效存储类型吗?
- 缓存和访问数据库数据的有效方法?
- 如何有效地读取和存储 1GB 文本文件中的数据
- 在使用 0MQ 异步接收时异步发送数据的最有效方法是什么?
- Windivert-修改数据包数据/有效载荷内容
- GLSL 计算着色器 使用查找表设置缓冲区会导致不写入任何数据,与其他数据设置相同的缓冲区有效
- 为什么指针>数据有效,而 *double_pointer->data 不起作用?
- 如何在 c++ 和 java 之间通过 udp 有效地发送数据数组
- DPDK:修改捕获的数据包标头的最有效方法