在清洁度和速度方面,动态与静态内存的最佳实践

时间:2009-07-24 07:55:21

标签: c++ memory-management

我有一个名为x的数组,其大小为6 * sizeof(float)。我知道声明:

float x[6];

将在堆栈内存中为x分配6 * sizeof(float)。但是,如果我执行以下操作:

float *x;   // in class definition

x = new float[6];   // in class constructor

delete [] x;    // in class destructor

我会将6 * sizeof(float)的动态内存分配给x。如果x的大小在类的生命周期中没有变化,就清洁度和速度的最佳实践而言(我隐约记得,如果不正确的话,堆栈内存操作比动态内存操作更快),我应该确定x是静态而不是动态分配的内存?提前谢谢。

11 个答案:

答案 0 :(得分:8)

声明固定大小的数组肯定会更快。每个单独的动态分配都需要找到一个未占用的块,而且速度不是很快。

因此,如果您真的关心速度(已分析),则规则是您不需要动态分配 - 请勿使用它。如果您需要它 - 请考虑分配多少,因为重新分配也不是很快。

答案 1 :(得分:3)

使用数组成员将更干净(更简洁,更不容易出错)并且更快,因为不需要调用分配和释放功能。您还倾向于改善所分配结构的“参考局部性”。

为这样的成员使用动态分配的内存的两个主要原因是所需大小仅在运行时已知,或者所需大小很大且已知这将对可用堆栈产生重大影响目标平台上的空间。

答案 2 :(得分:2)

堆栈上的TBH数据通常位于缓存中,因此速度更快。但是,如果您动态分配一次然后定期使用它,它也将被缓存,因此速度非常快。

重要的是避免定期分配和解除分配(即每次调用函数时)。如果你只是做了常规的分配和解除分配(即只分配和释放一次),那么堆栈和堆分配的数组将完全相同的预先形成。

答案 3 :(得分:1)

是的,静态声明数组的速度会更快。

这很容易测试,只需编写一个简单的包装循环来实例化这些对象的X个数。您还可以单步执行机器代码,并查看动态分配内存所需的大量OPCODE。

答案 4 :(得分:1)

静态分配更快(无需询问内存)并且您无法忘记删除它或使用不正确的删除操作符删除它(删除而不是删除[])。

构建动态/堆数据的用法包括以下步骤:

  • 要求内存分配对象(调用new运算符)。如果没有内存,则新运算符将抛出bad_alloc异常。
  • 使用默认构造函数创建对象(也由new完成)
  • 由用户释放内存(通过删除/删除[]运算符) - 删除将调用 对象析构函数。在这里,用户可以犯很多错误:
    • 忘记致电删除 - 这会导致内存泄漏
    • 调用不正确的删除操作符(例如删除而不是删除[]) - 会发生错误的事情
    • 要求删除两次 - 可能会发生不好的事情

使用静态对象/对象数组时,无需分配内存并由用户释放。这使代码更简单,更不容易出错。

总而言之,如果您在编译时知道数组上的大小并且您对内存无关(可能在运行时我将不使用数组中的条目),静态数组显然是首选的。 对于动态分配的数据,值得寻找智能指针(here

答案 5 :(得分:1)

不要混淆以下情况:

int global_x[6];        // an array with static storage duration

struct Foo {
    int *pointer_x;     // a pointer member in instance data
    int member_x[6];    // an array in instance data
    Foo() { 
        pointer_x = new int[6];    // a heap-allocated array
    }
    ~Foo() { delete[] pointer_x; }
};

int main() {
    int auto_x[6];           // an array on the stack (automatic variable)
    Foo auto_f;              // a Foo on the stack
    Foo *dyn_f = new Foo();  // a heap-allocated Foo.
}

现在:

  • auto_f.member_x在堆栈中,因为auto_f在堆栈中。
  • (*dyn_f).member_x在堆上,因为*dyn_f在堆上。
  • 对于两个Foo对象,pointer_x指向堆分配的数组。
  • global_x位于某些数据部分,操作系统或运行时每次运行程序时都会创建该部分。这可能是也可能不是与动态分配相同的堆,通常不重要。

因此无论它是否在堆上,member_x在长度始终为6的情况下比pointer_x更好,因为:

  • 代码少,容易出错。
  • 如果对象是堆分配的,则对象只需要一次分配,而不是2。
  • 如果对象在堆栈中,则您的对象不需要堆分配。
  • 由于分配较少,因此总共使用较少的内存,并且因为不需要存储指针值。

更喜欢pointer_x的理由:

  • 如果您需要在对象的生命周期内重新分配。
  • 如果不同的对象需要不同大小的数组(可能基于构造函数参数)。
  • 如果Foo对象将被放置在堆栈上,但是数组太大以至于它不适合堆栈。例如,如果你有1MB的堆栈,那么就不能使用包含int[262144]的自动变量。

答案 6 :(得分:0)

组合更高效,速度更快,内存开销更低,内存碎片更少。

你可以这样做:

template <int SZ = 6>
class Whatever {
   ...
   float floats[SZ];
};

答案 7 :(得分:0)

尽可能使用堆栈分配的内存。它将使您免于解除内存释放,虚拟地址空间碎片等问题。此外,与动态内存分配相比,它更快。

答案 8 :(得分:0)

如果您静态分配arraty,则只会有一个实例。使用类的关键是你需要多个实例。根本不需要动态分配数组:

class A {
   ...
   private:
      float x[8];
};

是你想要的。

答案 9 :(得分:0)

这里有更多变数:

  1. 数组的大小与堆栈的大小:与免费存储相比,堆栈大小非常小(例如,1MB到30MB)。堆栈上的大块会导致堆栈溢出

  2. 您需要的阵列数量:大量小阵列

  3. 数组的生命周期:如果它只在函数内部需要,那么堆栈非常方便。如果您需要 函数退出后,必须在堆上分配它。

  4. 垃圾收集:如果你在堆上分配它,你需要手动清理它,或者让一些智能指针为你做好工作。

答案 10 :(得分:0)

如另一个回复中所述,无法在堆栈上分配大对象,因为您不确定堆栈大小是多少。为了便于携带,应始终在堆上分配大对象或具有可变大小的对象。

操作系统现在提供的malloc / new例程中有很多开发(例如,Solaris的libumem)。动态内存分配通常不是瓶颈。