C++:静态指针、静态对象和动态内存分配

C++: Static pointers, static objects and dynamic memory allocation

考虑以下代码段:

#include <iostream>
using namespace std;

class p
{
    public:
    int* q;
    p()
    {
        q = new int(100);
    }
    ~p(){
        delete q;
    }
};

static p* p1 = new p();
static p p2;

int main() {
    // your code goes here
    std::cout << *(p1->q);
    std::cout << *(p2.q);

    delete p1;
}

p1 和 p2 是静态变量,它们必须存储在静态段中。

  1. 既然p1是一个指针,那么静态段甚至是它所指向的对象中存储的只是指针地址吗?

  2. p2是一个普通的静态对象,但是它包含了一个动态分配的成员变量q,那么q是不是也存储在静态段中呢?

  1. p1 是一个指针,它存储在静态段中(我不确定这是正确的术语),p1 指向的对象或内存在堆上。

  2. p2 是一个对象,它存储在静态段中。 qp2内部的指针,q指向的对象或内存在堆上。

您有两个 静态分配的对象 ,一个名为 p1 的指针和一个名为 p2 的类型 p 的实例。

程序中有两个地方可以进行动态分配:在 class p 的构造函数中和初始化静态变量 p1 时。

只要程序 运行s,静态分配的对象 p1(指针)和 p2(class 实例)就存在。区分仅包含地址的指针 p1 与位于该地址的 class 实例 很重要。(该实例将在 运行 处创建时间 new p())。指针和 "pointee" 可以有独立的生命周期;两者相互独立存在。该指针可能存在但不指向任何东西,并且 new p() 调用创建的对象可能存在的时间比指向它的任何指针都长。1

这是程序启动时展开的事件序列。静态变量的初始化在 C++11 标准的第 3.6.2 节中指定。

  1. 变量分配静态存储持续时间,此处p1p2 .一个工作模型是内存是程序的一部分。

  2. 这些变量的归零。 "Variables with static storage duration [...] shall be zero-initialized before any other initialization takes place." 指针 p1 以及 p2 现在所在的内存由全为零的字节组成。

  3. 这些变量的动态(即运行时间)初始化按照它们定义的顺序:

    • 指针 p1 的初始化从调用 new p() 开始。
      • p 类型的新对象的内存是使用标准分配器动态分配的 ("on the heap")。内存的内容未初始化且未知。该对象没有名称,所以我们称它为 x.
      • x' 执行构造函数以对其进行初始化。
        • 构造函数为迄今未初始化的成员变量赋值x.qx.qx 的一部分,因此驻留在之前动态分配的内存中。
        • 赋值的右侧是对 new 的另一个调用,这次是一个 int。标准分配器为初始化为 100 的 int 动态分配内存。
        • new的return值为int所在的内存地址,赋值给int指针x.q
      • x'构造函数returns,new p()returns是x所在的内存地址。
      • 此 return 值被分配给迄今为止零初始化的 p1,它现在指向我们称为 x.
      • 的未命名 p 实例
    • p2. 的初始化 p2 的构造函数被执行,它与上面的 x 的构造函数执行相同的操作:它为导致动态内存分配的 int 调用 new,用 100 初始化它并将 int 的内存位置的地址分配给 p2.q.

就内存位置和对象之间的关系而言,结果如下图所示。

这应该有助于回答您的问题:

    如果您愿意,
  1. p1 在 "static segment" 中,但它指向的对象已在 运行 时间通过调用 new 动态分配。
  2. 静态对象p2包含"a dynamically allocated member variable q"。这句话混淆了成员变量——一个名为 q 的指针——与 q 指向的对象 是一个动态分配的 int。成员变量 q 存储在 class p 的包含实例所在的位置;事实上,它 该实例中的唯一数据。 (尝试 sizeof(p)!)任何实例的成员 q 指向的对象始终是动态分配的 int(好吧,直到某些恶意程序员为您的 public q).


1这会构成内存泄漏,因为一个地址已经丢失的动态分配的对象永远不会被程序删除。