c++ 堆栈与类静态对象

c++ 堆栈与类静态对象

堆,英文是 heap,在内存管理的语境下,指的是动态分配内存的区域。这个堆跟数据结构里的堆不是一回事。这里的内存,被分配之后需要手工释放,否则,就会造成内存泄漏。

C++ 标准里一个相关概念是自由存储区,英文是 free store,特指使用 new 和 delete 来分配和释放内存的区域。一般而言,这是堆的一个子集:

  • newdelete 操作的区域是 free store

  • mallocfree操作的区域是 heap

    new delete 通常底层使用 mallocfree 来实现,所以 free store 也是 heap。鉴于对其区分的实际意义并不大,在本专栏里,除非另有特殊说明,我会只使用堆这一术语。

栈,英文是 stack,在内存管理的语境下,指的是函数调用过程中产生的本地变量和调用数据的区域。这个栈和数据结构里的栈高度相似,都满足“后进先出”(last-in-first-out 或 LIFO)。
RAII,完整的英文是 Resource Acquisition Is Initialization,是 C++ 所特有的资源管理方式。有少量其他语言,如 D、Ada 和 Rust 也采纳了 RAII,但主流的编程语言中, C++ 是唯一一个依赖 RAII 来做资源管理的。RAII 依托栈和析构函数,来对所有的资源——包括堆内存在内——进行管理。

对 RAII 的使用,使得 C++ 不需要类似于 Java 那样的垃圾收集方法,也能有效地对内存进行管理。RAII 的存在,也是垃圾收集虽然理论上可以在 C++ 使用,但从来没有真正流行过的主要原因。接下来,我将会对堆、栈和 RAII 进行深入的探讨。

声明私有析构函数

当我们规定类只能在上分配内存时,就可以将析构函数声明为私有的。

class alloc

{
public:

    alloc():

private:

   ~alloc();

};
如果在栈上分配空间,类在离开作用域时会调用析构函数释放空间,此时无法调用私有的析构函数。

如果在堆上分配空间,只有在delete时才会调用析构函数。

c++ 运行时候内存分配

一个由C/C++编译的程序运行时占用的内存分为以下几个部分
1
、栈区(stack 程序运行时自动分配释放 ,存放函数的参数值,局部变量的值等。其*
操作方式类似于数据结构中的栈。
2*、堆区(heap 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回*
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表(仅限于C++java有GC,方式不一样),呵呵。
3、全局区(静态区)(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
4*、文字常量区 常量字符串就是放在这里的。 程序结束后由系统释放
5
、程序代码区*—*存放函数体的二进制代码。

img

静态成员

静态成员变量有以下特点:

  1. 静态成员变量是该类的所有对象所共有的。对于普通成员变量,每个类对象都有自己的一份拷贝。而静态成员变量一共就一份,无论这个类的对象被定义了多少个,静态成员变量只分配一次内存,由该类的所有对象共享访问。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;
  2. 因为静态数据成员在全局数据区分配内存,由本类的所有对象共享,所以,它不属于特定的类对象,不占用对象的内存,而是在所有对象之外开辟内存,在没有产生类对象时其作用域就可见。因此,在没有类的实例存在时,静态成员变量就已经存在,我们就可以操作它;

因为它没有this指针,所以不是类对象也可以进行调用

  1. 静态成员变量存储在全局数据区。static 成员变量的内存空间既不是在声明类时分配,也不是在创建对象时分配,而是在初始化时分配

    静态成员变量必须初始化,而且只能在类体外进行。否则,编译能通过,链接不能通过。在Example 5中,语句int Myclass::Sum=0;是定义并初始化静态成员变量。初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化,一般是 0。静态数据区的变量都有默认的初始值,而动态数据区(堆区、栈区)的变量默认是垃圾值。
  2. static 成员变量和普通 static 变量一样,编译时在静态数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。

  3. 静态数据成员初始化与一般数据成员初始化不同。初始化时可以不加 static,但必须要有数据类型。被 private、protected、public 修饰的 static 成员变量都可以用这种方式初始化。静态数据成员初始化的格式为:<数据类型><类名>::<静态数据成员名>=<值>

  4. 类的静态成员变量访问形式1:<类对象名>.<静态数据成员名>

  5. 类的静态成员变量访问形式2:<类类型名>::<静态数据成员名>,也即,静态成员不需要通过对象就能访问。

  6. 静态数据成员和普通数据成员一样遵从public,protected,private访问规则;

  7. 如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员 ;

  8. sizeof 运算符不会计算 静态成员变量。

2.(面向对象的) 静态成员函数

与静态成员变量类似,我们也可以声明一个静态成员函数。

静态成员函数为类服务而不是为某一个类的具体对象服务。静态成员函数与静态成员变量一样,都是类的内部实现,属于类定义的一部分。普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。 **普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体地属于类的某个具体对象的**。当函数被调用时,系统会把当前对象的起始地址赋给 this 指针。通常情况下,this是缺省的。==如函数fn()实际上是this->fn()==。

与普通函数相比,静态成员函数属于类本身,而不作用于对象,*因此它不具有this指针*。正因为它没有指向某一个对象,所以它无法访问属于类对象的非静态成员变量和非静态成员函数,它只能调用其余的静态成员函数和静态成员变量。>从另一个角度来看,由于静态成员函数和静态成员变量在类实例化之前就已经存在可以访问,而此时非静态成员还是不存在的,因此静态成员不能访问非静态成员。

静态成员函数的特点:

    1. 出现在类体外的函数定义不能指定关键字static;
    1. 静态成员之间可以相互访问,即静态成员函数(仅)可以访问静态成员变量、静态成员函数;
    1. 静态成员函数不能访问非静态成员函数和非静态成员变量;
    1. 非静态成员函数可以任意地访问静态成员函数和静态数据成员;
    1. 由于没有this指针的额外开销,静态成员函数与类的全局函数相比速度上会稍快;
    1. 调用静态成员函数,两种方式:

    通过成员访问操作符(.)和(->),也即通过类对象或指向类对象的指针调用静态成员函数。

    直接通过类来调用静态成员函数。<类名>::<静态成员函数名>(<参数表>)。也即,静态成员不需要通过对象就能访问。

3. (面向过程的)静态全局变量

在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。

静态全局变量有以下特点:

  1. 该变量在全局数据区分配内存;
  2. 未经初始化的静态全局变量会被程序自动初始化为0(自动变量的自动初始化值是随机的);
  3. 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;
  4. 静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量。对于一个完整的程序,在内存中的分布情况如下:【代码区】【全局数据区】【堆区】【栈区】,一般程序的由new产生的动态数据存放在堆区,函数内部的自动变量存放在栈区,静态数据(即使是函数内部的静态局部变量)存放在全局数据区。自动变量一般会随着函数的退出而释放空间,而全局数据区的数据并不会因为函数的退出而释放空间

4. (面向过程的)静态局部变量

在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。

通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。

但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,这给程序的维护带来不便。

静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值

静态局部变量有以下特点:

  1. 静态局部变量在全局数据区分配内存;
  2. 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
  3. 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
  4. 静态局部变量始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束

5. (面向过程的)静态函数

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用

定义静态函数的好处:(类似于静态全局变量)

  1. 静态函数不能被其它文件所用;
  2. 其它文件中可以定义相同名字的函数,不会发生冲突;

猜你喜欢

转载自blog.csdn.net/ahelloyou/article/details/113508374