I implement a class that stores a number of Parent
objects inside an std::map
. Each Parent
has a Child
. That Child
has a pointer to it's Parent
, which is set in Parent
's constructor.
It goes like this: I call std::map::operator[]
, it calls Parent
's constructor, which sets child.parent
to this
, and returns me the Parent
. Should be okay, but if you'd compare the returned Parent
's address with the address that it's child
stores, they wouldn't match. Meaning that the Child
has an invalid pointer.
So when I initialize a Parent
through std::map::operator[]
, it's Child
's parent
pointer is invalid.
A little demo:
//
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <cstdlib>
// Just some forward declaration
struct Parent;
/**
* Child dummy (just to model my data structure)
*/
struct Child
{
/**
* Pointer to it's parent (that gets initialized in Parent's constructor)
*/
Parent * parent;
/**
* Original parent's uid
*/
int originalParentUniqueId;
};
/**
* Parent dummy to model my data scructure
*/
struct Parent
{
/**
* Child object that *should* reference (this) parent through a pointer
*/
Child child;
/**
* Some parent field for demonstration
*/
const char * someParentData;
/**
* What's our name?
*/
int uniqueId;
Parent()
{
uniqueId = std::rand();
// Luke, I'm your father!
child.parent = this;
child.originalParentUniqueId = uniqueId;
// We'll be GLaD we get burned (somewhere inside std::map)
someParentData = "The cake is a lie.";
// Our child will be adopted by another Parent, but he will always remember us.
// (by keeping that child.parent ptr pointing at THIS instance)
}
};
//
// Test case
//
#include <map>
#include <ctime>
#include <iostream>
typedef std::map<int, Parent> test_map_t;
int _tmain(int argc, _TCHAR* argv[])
{
std::srand( std::time( NULL ) );
//
// Testing without std::map first.
//
Parent testParent;
if( testParent.child.parent != &testParent )
{
std::cout << "The pointers do NOT match. Impossiburu!\n"; // can't get here
}
else
std::cout << "The pointers match. Things work as expected.\n";
std::cout << "\n";
//
// Let's test std::map now
//
test_map_t testMap;
Parent parent = testMap[ 42 ]; // life, the universe and everything...
if( parent.child.parent != &parent )
{
std::cout << "The pointers do NOT match.\nMight crash in case of access violation...\n";
std::cin.get();
}
else
std::cout << "The pointers match. Houston, we have a problem.\n"; // can't get here
std::cout
<< "parent.uniqueId: \""
<< parent.uniqueId << "\"\n"
<< "parent.child.originalParentUniqueId: \""
<< parent.child.originalParentUniqueId << "\"\n\n"
;
std::cout
<< "parent.someParentData: \""
<< parent.someParentData << "\"\n"
<< "parent.child.getSomeParentData(): \""
<< parent.child.parent->someParentData << "\"\n"
;
std::cin.get();
return 0;
}
Output:
The pointers match. Things work as expected.
The pointers do NOT match.
Might crash in case of access violation...
parent.uniqueId: "1234321"
parent.child.originalParentUniqueId: "1234321" <- Match.
parent.someParentData: "The cake is a lie."
parent.child.getSomeParentData(): " <- Access violation reading
address 0xcccccccc (literally),
no further output
Access violation gets raised when accessing parent.child.parent -> someParentData
. Debugging this issue on a real app showed that the Parent
returned from std::map::operator[]
is different from the one that was used to create a Child
, but the Child
is the same object that was instantiated during the original Parent
's construction.
It goes like this: you call operator[]
, it creates parent_A
, parent_A
creates a child_A
and sets it's child_A.parent
pointer to &parent_A
. Then for some reason parent_A
gets destroyed, and parent_B
takes it's place. But it stores the same old data including child_A
, who's child_A.parent
still points to parent_A
.
The question is, why does it happen and how to solve this issue?
One of the project's requirements is use of vs2005 with it's native compiler.
Thank you in advance!