1

我是一个长期的读者,也是第一次发帖……我已经搜索了很长时间,很难找到现在真正让我难以置信的事情的答案。我一定错过了一些东西,因为我相信这应该有效......

我正在尝试创建一个数据表类,该类将包含它自己的传递给它的对象的副本。我决定使用 std::map 来包含这些数据。请参见下面的示例代码:

typedef std::map <std::string, myVar *> myVarContainer;

class myObj
{
    public:
        myObj(void);
        virtual ~myObj(void);

        void setVar(std::string Key, myVar & Var);

        myVar * getVar(std::string Key);

        void release()
        {
            for (myVarContainer::iterator i = VarContainer->begin(); i != VarContainer->end(); ++i)
            {
                delete (i->second);
            }

            VarContainer->clear();
        };

        myVarContainer * VarContainer;

};

typedef std::map <myVar, myObj *> myRow;

class myTable
{
    public:
        myTable(void);
        virtual ~myTable(void);

        void addDataPoint(myVar RowID, myVar ColID, myObj * Data)
        {
            std::map <myVar, myRow *>::iterator i = m_Rows->find(RowID);

            if (i == m_Rows->end())
            {
                m_Rows->insert(make_pair(RowID, new myRow()));
            }
            i = m_Rows->find(RowID);

            // i thought the below line would be creating a copy of the data?
            // I thought this logic went:
            // 1. create a new object copied from the value of 'Data'
            // 2. return a pointer to this object and pair with the 'colID'
            // 3. make this into a pair and insert into the main map
            i->second->insert(make_pair(ColID, new myObj(*Data)));
        };

    protected:

        std::map <myVar, myRow *> * m_Rows;
}


int main()
{

    myVar a, b, c, d;

    myObj * o = new myObj();

    o->setVar("test", a);
    o->setVar("test2", b);

    myTable * tab = new myTable();

    myVar x1, y1, x2;

    tab->addDataPoint(y1, x1, o);

    o->release(); // this clears out both 'o' and the values in 'tab'!?!?

    //at this point tab has no data in its object at y1,x1???

    o->setVar("test3", c);
    o->setVar("test4", d);

    tab->addDataPoint(y1, x2, o);
}

我注意到的是我的数据被删除得太早了。我相信我错过了一些东西...我以为我正在创建指针引用的数据的副本,然后在我的地图中存储一个新实例化的指针...有什么想法吗?我很感激任何帮助!

4

2 回答 2

0

看来您确实在创建对象的副本,但是当您 release() 时,您正在释放 VarContainer(删除所有项目并使用 clear()),因此您之前创建的副本(带有指针的副本,而不是实际的容器)留下一个指向空容器的指针。

于 2012-10-08T19:18:07.537 回答
0

因此,在容器中使用(拥有)原始指针的问题之一是您需要自己手动删除实例。我认为就是这样myObj::~myObj做的(在删除容器本身之前迭代容器删除所有元素)。

该行:

i->second->insert(make_pair(ColID, new myObj(*Data)));

是从 Data 中复制构造一个 myObj。

不幸的是,因为您没有为 myObj 定义复制构造函数,所以编译器将为您生成一个复制构造函数,它只会复制指向该VarContainer成员的指针。它不会创建地图的新副本或它在内部引用的任何内容。创建副本后,您将拥有两个实例,它们都指向同一个容器,并且两个实例都认为它们拥有它。当第一个被破坏时,它似乎没问题,但实际上让另一个实例指向已释放的内存。一旦寿命最长的实例试图使用这个容器指针做任何事情,就会发生一些不好的事情。

您可以通过按值而不是指针存储映射来解决此问题:

typedef std::map<std::string, myVar> myVarContainer;
typedef std::map<myVar, myObj> myRow;

也更改myObj::VarContainer为非分配成员。这意味着现在所有内容都被正确复制,并且副本不会引用原始文件中的任何内容。

请注意,您也可以使用智能指针(例如std::shared_ptr)而不是原始指针,但您仍然需要小心,因为虽然复制是安全的,但它们与原始指针共享数据,这可能不是您所期望的。

你应该看看以下内容:

http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming )

于 2012-10-08T19:57:49.937 回答