c++ - 我如何让连续内存以安全的方式表现出多态性?

标签 c++ memory-management memory-leaks

我有一大群对象,我按顺序迭代了数万到数十万(在某些情况下,数百万)次。我想连续存储这些对象以减少缓存未命中的次数。我还想从中获得多态行为。我的想法是分配一大块内存,就地构建它们,然后将指向每个对象的指针存储在一个 vector 中。一些示例代码如下所示:

#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/手动调用析构函数更简单的分配/取消分配方法?

最佳答案

使用 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 安装尚未更新以说明那个。

关于c++ - 我如何让连续内存以安全的方式表现出多态性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32427205/

相关文章:

c++ - 通过 decltype 表达式调用时 static_assert 是否应该工作?

c++ double 到字节数组

安卓内存分配

ios - 是否有任何文档/页面详细解释 monotuch 如何处理非托管(NSObject)对象的内存管理/对象生命周期

iphone - 仪器内存泄漏 iPhone

c++ - 由于缓存未命中,我应该避免静态编译吗?

C++ - 转换 std::basic_string<char>

ios - NSMutableDictionary initWithContentsOfFile 中的内存泄漏

java - 尝试通过 python 脚本启动 Minecraft 服务器时出现内存问题

java - JVM堆不断增加。为什么?