2015-01-29 7 views
0

Я реализую класс, в котором хранится количество объектов Parent внутри std::map. Каждый Parent имеет Child. То, что Child имеет указатель на его Parent, который установлен в конструкторе Parent.Почему std :: map восстанавливает его значение, когда он создается с помощью оператора []

Это выглядит следующим образом: я называю std::map::operator[], он вызывает конструктор Parent «s, который устанавливает child.parent к this, и возвращает мне Parent. Должно быть хорошо, но если вы сравните возвращенный адрес Parent с адресом, который находится в магазинах child, они не совпадают. Это означает, что Child имеет недопустимый указатель.

Так что, когда я инициализирую Parent через std::map::operator[], это Child's parent Указатель недействителен.

Немного демо:

// 
// 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; 
} 

Выход:

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 

Нарушение прав доступа получает возникает при доступе к parent.child.parent -> someParentData. Отладка этой проблемы в реальном приложении показала, что Parent, возвращенный с std::map::operator[], отличается от того, который использовался для создания Child, но Child - это тот же объект, который был создан во время первоначальной конструкции Parent.

Это выглядит следующим образом: вы называете operator[], он создает parent_A, parent_A создает child_A и устанавливает это child_A.parent указатель на &parent_A. Зато почему-то parent_A уничтожен, и parent_B занимает свое место. Но он хранит те же старые данные, в том числе child_A, кто child_A.parent по-прежнему указывает на parent_A.

Вопрос в том, почему это происходит и как решить эту проблему?

Одним из требований проекта является использование vs2005 с его собственным компилятором.

Спасибо заранее!

+1

Класс 'Parent' не реализует« Правило 3 »: http: //stackoverflow.com/questions/4172722/what-is-the-rule-of-three – PaulMcKenzie

ответ

1

При использовании:

Parent parent = testMap[ 42 ]; 

Вы получаете копию Parent на карте. Конечно, child в этом объекте parent указывает на Parent, который не существует.

Вам необходимо реализовать конструктор копирования Parent, который делает правильную вещь для содержащегося child.

Parent(Parent const& copy) 
{ 
    uniqueId = std::rand(); 

    child.parent = this; 
    child.originalParentUniqueId = uniqueId; 
} 

Ммм ... не уверен, что можно использовать из copy.

+0

Спасибо за ваш ответ!Означает ли это, что изменение полей 'copy' не повлияет на исходный родитель, который хранится на карте? Как получить ссылку на него вместо копии? – GeorgeK

+2

Вы можете получить ссылку, используя 'Parent & parent = testMap [42];'. –