C++ 使用指针/引用影响本地成员的多态调用

C++ 使用指针/引用影响本地成员的多态调用,c++,polymorphism,C++,Polymorphism,我今天在编写代码时遇到了一个奇怪的情况,我希望有人能解释为什么会发生这种情况 我有一个指向某个基类的指针列表: std::list<BaseClass*> m_list; 然后我将这个基类转换成它的一个子类。(我认为这就是奇怪之处) ChildClass使用基类复制构造函数复制所有基类字段 现在,对于这个子类,我调用其中一个基类方法来设置基类中的字段 pBase->SetSomeIntMember(10); 现在,如果我检查这个int值,它会像预期的那样是10,但它似乎只是

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

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

std::list<BaseClass*> m_list;
然后我将这个基类转换成它的一个子类。(我认为这就是奇怪之处)

ChildClass使用基类复制构造函数复制所有基类字段

现在,对于这个子类,我调用其中一个基类方法来设置基类中的字段

pBase->SetSomeIntMember(10);
现在,如果我检查这个int值,它会像预期的那样是10,但它似乎只是在本地更改它,因为如果我再次从列表中获取相同的子类,并检查它的int成员,它将保持不变

希望这不会太棘手。是什么让这一切发生的?在任何不涉及多态性的情况下,它显然不仅仅是本地更改,因为我们有一个指向类实例的指针。我猜当我创建一个新的ChildClass时,我是在踩指针,但它肯定会使列表中的基类成为一个ChildClass,因为虚拟方法仍然有效

 pBase = new ChildClass(pBase);
这不会“使列表中的基类成为子类”。它创建了一个新的ChildClass实例。只有在
ChildClass
的构造函数中对
pBase
所做的更改才能影响
pBase
之前指向的内容。(不能使一个类“成为子类的实例”。)

那一行代码根本不会改变
m_list
m_列表
仍然包含指向原始
基类
对象的指针

这不会“使列表中的基类成为子类”。它创建了一个新的ChildClass实例。只有在
ChildClass
的构造函数中对
pBase
所做的更改才能影响
pBase
之前指向的内容。(不能使一个类“成为子类的实例”。)


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

乍看之下,您只是将新分配的指针按值分配给pBase。list元素实际上是指针地址,它被value复制到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

乍一看,您只是将新分配的指针按值分配给pBase。list元素实际上是指针地址,它被value复制到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

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

就是

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

Baseclass*& pBase_r = m_list.front();
pBase_r = new ChildClass(*pBase_r);
请记住,如果要更新原始值,则需要使用引用或指针

#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
}
注意

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

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


因此,您的list类应该是
std::list
。这里的另一个优点是,您可以使用智能指针的实例而不是引用,而不会弄乱内部引用计数。

您可以复制指针的值,而不是指针的引用

就是

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

Baseclass*& pBase_r = m_list.front();
pBase_r = new ChildClass(*pBase_r);
请记住,如果要更新原始值,则需要使用引用或指针

#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
}
注意

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

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


因此,您的list类应该是
std::list
。这里的另一个优点是,您可以使用智能指针的实例而不是引用,而不会弄乱内部引用计数。

正如其他人所指出的,您的问题是,您只是在修改指针的本地副本,而不是它实际指向的对象

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

#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
}

正如其他人所指出的,您的问题在于您只是在修改指针的本地副本,而不是它实际指向的对象

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

#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
}

ChildClass->SetSomeIntMember(10)不是有效的代码,你的意思是
pBase->…
?确实是这样。编辑好了,谢谢!另外,
pBase=newchildclass(pBase)
不会调用复制构造函数,原因有两个:1)复制构造函数采用
常量&
参数,而不是指针;参数应为相同类型(或派生类型),而不是基类对象。除非可以从参数中获得一个
const ChildClass&
,否则复制构造函数将不会被调用。我将取消对指针的引用,以实际通过引用传递来调用复制构造函数。编辑为正确。
ChildClass->SetSomeIntMember(10)不是有效的代码,你的意思是
pBase->…
?确实是这样。编辑好了,谢谢!另外,
pBase=newchildclass(pBase)
不会调用复制构造函数,原因有两个:1)复制构造函数采用
常量&
参数,而不是指针;2)论点应该是相同的