《Effective C++》 条款04:Stack与Heap / 内存管理

阅读effective c++ 04 (30页) 提到的static对象和堆与栈对象。“不同编译单元内定义的non-local static对象”。

Stack与Heap / 内存管理

stack

stack是在某个作用域内的一块内存空间。当调用函数时,函数本身会形成一个栈来保存函数的参数和返回地址。函数体内声明的所有变量,都来自stack的内存。

heap / system heap

堆是操作系统提供的全局global内存空间。程序动态分配dynamic allocated时,从堆里面取一些区块blocks。

stack和heap的内存例子 / 类外的static声明

1
2
3
4
5
6
7
8
9
10
11
class Complex { … };
...
{
Complex c1(1,2);//stack/non-static local
static Complex c2(1,2);//static local

Complex* p = new Complex(3);
...
delete p;
}
Complex c3(1,2);//global/static
  • stack / 构造函数型:
    • 局部对象 c1 的内存来自stack,生命周期在scope结束时结束,所以又称auto object,因为会被自动清理。
  • heap / new型 动态分配:
    • 动态分配的临时对象Complex(3) ,占用空间来自heap。指针p指向这个临时对象,p的生命在它被delete的时候结束。如果当作用域结束,p指的堆对象依然存在,但是p的生命结束了,作用域外再也没有p,无法delete p,会导致内存泄漏leak。
  • static 对象:在内存中。程序结束,自动销毁
    • local static object。 c2 。在函数第一次调用时构造初始化。在程序结束之后自动析构。
    • non-local static object
      • global object。c3。main调用之前被构造初始化。在程序结束之后自动析构。
      • 定义于namespace作用域的对象。main调用之前被构造初始化。在程序结束之后自动析构。

类外的static声明

两个编译单元中的non-local static object相互引用

注意:在同一个文件或不同编译单元(不同文件)中,如果存在多个non-local static object,它们都是在主函数调用之前被构造的,但是它们之间的构造顺序时不定的。即对编译器来说,静态成员对象之间的初始化顺序和析构顺序是一个未定义的行为。

因此,不能用某个non-local static object去初始化non-local static object,无论这两个non-local static object在不在同一个编译单元中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class FileSystem

{

public: …

std::size_t numDisks() const;

};

extern FileSystem tfs;

//另一编译单元

class Directory

{

public:

Directory(params);

};

Directory::Directory(params)

{

std::size_t disks = tfs.numDisks();//使用另一个编译单元的静态变量

}
Directory tempDir (params);

由于编译器没有定义non-local static object之间的构造顺序,所以有可能类tfs还没有被构造,所以程序可能会报错。

解决方法

用local static对象替换non-local static对象。

C++保证,函数内的local static 对象会在该函数被调用期间,首次遇上该对象定义式时被初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class FileSystem {…};

FileSystem& tfs()

{

static FileSystem fs;

return fs;

}

//另一编译单元

class Directory {…};

Directory::Directory (params)

{

std::size_t disks = tfs().numDisks();//执行函数tfs时,对象fs肯定会被构造。

};

Directory& tempDir()

{

static Directory td;

return td;

}
Thanks for Support.