如何以一种安全的方式使连续的内存行为多态化?

时间:2015-09-06 19:05:35

标签: c++ memory-management memory-leaks

我有一大堆数千个对象,我按顺序迭代,数万到数十万(有时是数百万)次。我想连续存储这些对象,以减少我的缓存未命中数。我也希望从中获得多态行为。我的想法是分配一大块内存,在适当的位置构建它们,并在向量中存储指向每个对象的指针。一些示例代码如下所示:

#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

class Base
{
public:
  virtual ~Base() {}
  Base(int x) : x_(x) {}
  Base& operator=(const Base& b) { x_ = b.x(); return *this; }
  inline int x() const { return x_; }
  virtual void foo() const { cout << "Base::foo() = " << x() << "\n"; }
private:
  int x_;
};

class Derived : public Base
{
public:
  ~Derived() {}
  Derived(int x, int y) : Base(x), y_(y) {}
  Derived& operator=(const Derived& d) { Base::operator=(d); y_ = d.y(); 
                                         return *this; }
  inline int y() const { return y_; }
  void foo() const { cout << "Derived::foo() = " << x() << ", " << y() << 
                     "\n"; }
private:
  int y_;
};

constexpr size_t max_class_hierarchy_size()
{
  return (sizeof(Base) > sizeof(Derived)) ? sizeof(Base) : 
          sizeof(Derived);
}

enum class DynamicType : unsigned { BASE, DERIVED };

int main()
{
  const static unsigned int n = 5;
  const int xs[] = {1, 3, 2, -4, 5};
  const int ys[] = {-4, 7, 12, 15, 3};
  const DynamicType tps[] = {DynamicType::BASE, DynamicType::DERIVED, 
                             DynamicType::DERIVED, DynamicType::DERIVED,
                             DynamicType::BASE};

  cout << "sizeof(Base) = " << sizeof(Base) << "\n";
  cout << "sizeof(Derived) = " << sizeof(Derived) << "\n";
  cout << "max size() = " << max_class_hierarchy_size() << "\n";

  void* main_mem_pool = malloc(n * max_class_hierarchy_size());

  Base* mem_pool = static_cast<Base*>(main_mem_pool);
  vector<Base*> bs(n);

  for (unsigned i = 0; i < n; ++i)
  {
    bs[i] = mem_pool;
    switch(tps[i])
    {
      case DynamicType::BASE:
      {
        Base* new_loc_base = static_cast<Base*>(mem_pool);
        new (new_loc_base) Base(xs[i]);
        new_loc_base++;
        mem_pool = static_cast<Base*>(new_loc_base);
        break;
      }
      case DynamicType::DERIVED:
      {
        Derived* new_loc_derived = static_cast<Derived*>(mem_pool);
        new (new_loc_derived) Derived(xs[i], ys[i]);
        new_loc_derived++;
        mem_pool = static_cast<Base*>(new_loc_derived);
        break;
      }
      default:
        cerr << "Type: " << static_cast<unsigned>(tps[i]) 
             << " not defined. Exitting...\n";
        exit(1);
    }
  }

  for (const auto& b : bs) b->foo();

  for (int i = n-1; i >= 0; --i) bs[i]->~Base();
  free(main_mem_pool);

  return 0;
}

我为一个冗长的例子道歉,但这给了我期待的行为。唯一的问题是valgrind告诉我,我有内存泄漏。 Valgrinds输出说我有3个分配,只有2个解除分配。我不会看到它。

为什么这是内存泄漏?有没有更好的方法来解决这个问题?有没有比malloc / free / manual调用析构函数更简单的方法来分配/解除分配?

1 个答案:

答案 0 :(得分:1)

使用valgrind --leak-check=yes --leak-check=full选项运行此命令,因为valgrind的原始诊断建议您这样做,将向您显示额外的分配来自C ++标准库中的某些启动代码,并且与您的代码无关

运行时库在初始化时分配一些静态存储是很常见的,它们不会在终止时释放。你从valgrind得到的原始输出可能是这样的:

==8498== LEAK SUMMARY:
==8498==    definitely lost: 0 bytes in 0 blocks
==8498==    indirectly lost: 0 bytes in 0 blocks
==8498==      possibly lost: 0 bytes in 0 blocks
==8498==    still reachable: 72,704 bytes in 1 blocks
==8498==         suppressed: 0 bytes in 0 blocks

除非在前两个类别中出现某些内容,否则通常不是真正的内存泄漏。大多数发行版都使用抑制文件配置valgrind,该文件忽略标准系统库在初始化时捕获的已知内存块的警告,并且不需要在退出时进行清理。 Gnome库是最糟糕的攻击者,C ++标准库通常非常干净,但我想当前版本的libstdc++会留下一些东西,并且各种发行版valgrind安装尚未更新以解决这个问题。 / p>