【问题标题】:Derived Class Array not Formed未形成派生类数组
【发布时间】:2013-09-11 01:27:56
【问题描述】:

我在创建派生类对象数组时遇到了一个问题,我找不到这个问题的任何解释。问题如下:

我有一个抽象基类 RDMonitorBase 和一个派生类 RDMonitorHashTable。

在另一个类 RDMonitorPool 中,我有变量 RDMonitorBase * m_rd_monitors。

在 RDMonitorPool 构造函数中,我正在创建一个 RDMonitorHashTable 对象数组(使用新的) 并将它们分配给 m_rd_monitors。

在构造之后,只有偶数索引的 RDMonitorHashTable 对象被正确形成。具有奇数索引的对象在成员变量中有垃圾。我不知道为什么会这样。

代码和GDB在构建后立即断点处的输出如下:

RDMonitorBase 和 RDMonitorHashTable 类的相关部分如下。我在 Base 中有一个未在派生中实现的虚拟 Access() 方法,以及一个纯虚拟 RecordDistance() 方法。 Access() 调用 RecordDistance() 方法。我认为这应该没问题,当我创建简单示例时,类似的结构也起作用。

class RDMonitorBase {
 public:
  RDMonitorBase();
  virtual void StartMonitoring(const physical_address_t& target_address, const int core_id);
  virtual Index Access(const physical_address_t& address, const int core_id);
  bool IsActive() { return m_active; }
  static const Index TRACKING_NOT_COMPLETE = -1;

 protected:
  virtual void RecordDistance(const physical_address_t& address) = 0;
  const Index m_maximum_distance;
  bool m_active;
  physical_address_t m_target_address;
  Index m_distance;
  int m_core_id;
};

class RDMonitorHashTable: public RDMonitorBase {
 public:
  RDMonitorHashTable();
  virtual void StartMonitoring(const physical_address_t& target_address,
                           const int core_id);
 protected:
  virtual void RecordDistance(const physical_address_t& address);
  std::tr1::unordered_set<physical_address_t> m_address_set;
};

RDMonitorBase::RDMonitorBase():
  m_maximum_distance(32),
  m_active(false),
  m_target_address(0),
  m_distance(0),
  m_core_id(-1) { ; }

Index RDMonitorBase::Access(const physical_address_t& address,
                        const int core_id) {

  // ... some code
  RecordDistance(address);
  //... some other code 
  return m_distance;
}

RDMonitorHashTable::RDMonitorHashTable()
  :RDMonitorBase() { ; }

void RDMonitorHashTable::RecordDistance(const physical_address_t& address) {
  m_address_set.insert(address);
  m_distance = m_address_set.size();
}

现在在 RDMonitorPool 类的构造函数中,基本上执行了以下内容:

RDMonitorBase * m_rd_monitors = new (std::nothrow) RDMonitorHashTable[4096];

这一行执行后,当我尝试在 gdb 中打印对象时,我得到了这个:

(gdb) print *(m_rd_monitors + 0)
$2 = {_vptr.RDMonitorBase = 0x7ffd93cacc90, static TRACKING_NOT_COMPLETE = -1, m_maximum_distance = 32, m_active = false, m_target_address = 0, m_distance = 0, m_core_id = -1}
(gdb) print *(m_rd_monitors + 1)
$3 = {_vptr.RDMonitorBase = 0x1, static TRACKING_NOT_COMPLETE = -1, m_maximum_distance = 68540736, m_active = 11, m_target_address = 0, m_distance = 4611686019492741120, m_core_id = 11}
(gdb) print *(m_rd_monitors + 2)
$4 = {_vptr.RDMonitorBase = 0x7ffd93cacc90, static TRACKING_NOT_COMPLETE = -1, m_maximum_distance = 32, m_active = false, m_target_address = 0, m_distance = 0, m_core_id = -1}
(gdb) print *(m_rd_monitors + 3)
warning: can't find linker symbol for virtual table for `RDMonitorBase' value
$5 = warning: can't find linker symbol for virtual table for `RDMonitorBase' value
{_vptr.RDMonitorBase = 0x7ffda46b3e98, static TRACKING_NOT_COMPLETE = -1, m_maximum_distance = 68566480, m_active = 11, m_target_address = 0, m_distance = 4611686019492741120, m_core_id = 11}

因此,偶数索引值具有在构造函数中分配的正确值(例如,32 代表 m_maximum_distance)。但是奇数索引 objexxts 在 m_maximum_distance 和其他成员中有垃圾。

如果有人能解释为什么会发生这种情况,我将非常感激。谢谢!

【问题讨论】:

  • 我没有时间仔细看这个,但我怀疑问题是RDMonitorHashTableRDMonitorBase 的大小不同,因此*(m_rd_monitors + 1) 没有'实际上并不指向第二个元素,而是指向第一个元素中间的某个位置(即new 分配的对象是派生类型的正确大小,但由于指针指向基类型,它只添加 1) 时添加基本类型的大小。不知道为什么偶数索引值看起来合理 - 这可能只是内存布局的一些怪癖。
  • @BenS。这正是发生的事情,但即使偏移量在某种程度上完全相同并且它在内部起作用,依赖于此,不受支持并且行为未定义。
  • 非常感谢!你们俩都找到了正确的原因。基类指针算法不应用于派生类对象。学习到教训了。生活又好起来了!

标签: c++ arrays inheritance


【解决方案1】:

您的问题在这里得到解决:Base pointer to array of derived objects

问题是数组的类型是RDMonitorBase,所以当你尝试访问它的任何位置时,编译器会根据RDMonitorBase而不是RDMonitorHashTable来计算偏移量,从而有效地取消引用错误的内存点,从而切出不同类型的实际对象。从您的程序访问该数组的任何位置开始,它的行为就变得未定义。

最直接的替代方法是使用指向基类型的指针数组,并为每个指针分配一个动态构造的派生类对象。

RDMonitorBase* m_rd_monitors[4096];
for(std::size_t i = 0; i < 4096; ++i)
    m_rd_monitors[i] = new (std::nothrow) RDMonitorHashTable;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-11
    • 2010-10-20
    • 1970-01-01
    • 2014-10-18
    • 1970-01-01
    • 2016-03-27
    • 2021-12-15
    • 2018-11-30
    相关资源
    最近更新 更多