1

我今天在编码时遇到了一个奇怪的情况,我希望有人能解释为什么会这样。

我有一个指向某些基类的指针列表:

std::list<BaseClass*> m_list;

然后我从这个列表中得到一个 BaseClass 指针

BaseClass* pBase = m_list.front();

然后我把这个基类变成它的子类之一。(这就是我认为奇怪之处发挥作用的地方)

pBase = new ChildClass(*pBase);

ChildClass 使用 BaseClasses 复制构造函数来复制所有 BaseClasses 字段。

现在有了这个 ChildClass,我调用 BaseClasses 方法之一在 BaseClass 中设置一个字段。

pBase->SetSomeIntMember(10);

现在,如果我检查这个 int 值,它是 10,正如预期的那样,但它似乎只是在本地更改它,因为如果我再次从列表中获得相同的 ChildClass 并检查它的 int 成员,它将保持不变。

希望这不是太棘手。是什么导致这种情况发生?在任何不涉及多态性的情况下,它显然不仅是局部变化,因为我们有一个指向类实例的指针。我猜我在创建一个新的 ChildClass 时踩到了指针,但这肯定会使列表中的 BaseClass 成为 ChildClass,因为虚拟方法仍然有效。

4

4 回答 4

3
 pBase = new ChildClass(pBase);

这不会“使列表中的 BaseClass 成为 ChildClass”。它创建了一个的 ChildClass 实例。只有对pBasedone inChildClass的构造函数的更改会影响pbase之前指向的内容。(你不能让一个类“成为子类的一个实例”。)

那行代码根本没有改变m_listm_list仍然包含指向原始BaseClass对象的指针。

于 2012-05-13T06:08:46.720 回答
2

乍一看,您只是将新分配的指针按值分配给 pBase。列表元素实际上是按值复制到 pBase 的指针地址。列表元素实际上没有改变

试试这个

BaseClass** pBase = &(m_list.front());
BaseClass* pOld = *pBase;
*pBase = new ChildClass(**pBase); // you have a leak here of *pBase BTW 
deleteOrCleanup(pOld); // cleanup or delete the old pBase pointer 

//do what you were doing
于 2012-05-13T06:08:54.443 回答
2

您复制指针的值,而不是对指针的引用。

那是,

BaseClass* pBase = m_list.front();
pBase = new ChildClass(*pBase);

不一样

Baseclass*& pBase_r = m_list.front();
pBase_r = new ChildClass(*pBase_r);

请记住,如果要更新原始值,则需要使用引用或指针。

笔记

第二个示例包含内存泄漏,因为 的原始值在pBase之前被丢弃delete。为避免此类意外,请使用智能指针,例如std::shared_ptr<T>(C++11) 或boost::shared_ptr<T>代替T*.

不要使用,因为它的语义与 STL 容器std::auto_ptr<T>兼容。

所以你的列表类应该是std::list<std::shared_ptr<BaseClass>>. 这里的另一个优点是您可以使用智能指针的实例而不是引用,而不会弄乱内部引用计数。

于 2012-05-13T06:09:36.830 回答
2

正如其他人指出的那样,您的问题是您只是在修改指针的本地副本,而不是它实际指向的内容。

当您尝试替换容器元素时,不要将原始指针粘贴到容器中并不得不手动删除它们(或泄漏内存),而是使用智能指针。

#include <memory>
#include <list>
#include <iostream>

struct base
{
  base( int a )
  : x(a)
  {}

  int x;
};

struct derived : base
{
  derived( int a )
  : base(a)
  {}
};

int main()
{
  std::list<std::unique_ptr<base>> mylist;
  mylist.push_back( std::unique_ptr<base>( new derived(10) ) );

  auto pbase = mylist.front().get();    // get raw pointer to first element
  std::cout << pbase->x << std::endl;

  pbase = new derived( 10 * pbase->x ); // create a new derived object
  mylist.front().reset( pbase );        // replace the first element, previous 
                                        // element is deleted automatically

  pbase = mylist.front().get();
  std::cout << pbase->x << std::endl;

  // all allocated objects will be automatically deleted
  // when mylist goes out of scope
}

输出:

10
100
于 2012-05-13T06:28:13.637 回答