为什么std::map在通过operator[]创建的时候实例化它的值

Why does std::map reinstantiate it's value when it's created through operator[]

我实现了一个 class,它在 std::map 中存储了一些 Parent 个对象。每个 Parent 都有一个 ChildChild 有一个指向它的 Parent 的指针,它在 Parent 的构造函数中设置。

它是这样的:我调用 std::map::operator[],它调用 Parent 的构造函数,它将 child.parent 设置为 this,而 returns 我Parent。应该没问题,但如果您将返回的 Parent 的地址与其 child 存储的地址进行比较,它们将不匹配。这意味着 Child 有一个无效的指针。

所以当我通过std::map::operator[]初始化Parent时,Childparent指针无效。

一个小演示:

//
// 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 时引发访问冲突。在真实应用程序上调试此问题表明从 std::map::operator[] 返回的 Parent 与用于创建 Child 的不同,但 Child 是同一个对象这是在最初 Parent 的构造过程中实例化的。

它是这样的:你调用 operator[],它创建 parent_Aparent_A 创建一个 child_A 并将它的 child_A.parent 指针设置为 &parent_A。然后由于某种原因 parent_A 被摧毁, parent_B 取代了它。但它存储相同的旧数据,包括 child_Achild_A.parent 仍然指向 parent_A

问题是,为什么会发生这种情况以及如何解决这个问题?

该项目的要求之一是使用 vs2005 及其本机编译器。

提前致谢!

当您使用时:

Parent parent = testMap[ 42 ];

您正在获取地图中 Parent 的副本。当然,这个parent对象中的child指向的是不存在的Parent

您需要实现 Parent 的复制构造函数,它为包含的 child.

做正确的事情
Parent(Parent const& copy) 
{
    uniqueId = std::rand();

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

嗯...不确定 copy 中可以使用什么。