多态调用使用指针/引用实现本地成员

时间:2012-05-13 05:59:40

标签: c++ polymorphism

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

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

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 个答案:

答案 0 :(得分:3)

 pBase = new ChildClass(pBase);

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

这行代码根本不会改变m_listm_list仍然包含指向原始BaseClass对象的指针。

答案 1 :(得分:2)

首先看你只是按值将新分配的指针分配给pBase。 list元素实际上是通过值复制到pBase的指针地址。 list元素实际上没有改变

试试这个

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

答案 2 :(得分: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*

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

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

答案 3 :(得分: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