C++中的static静态的变量/函数/对象 与 内存关系透析

背景

内存中的存储区域如下:

栈区:由编译器自动分配和释放,主要存储 函数的参数值、函数内部的变量的值、函数调用的空间。从高地址向低地址申请;容量有限;速度较快;存储地址连续,会溢出。
堆区:是由程序员手动申请(new)与释放(delete)的内存区域。从低地址向高地址申请;内存空间大、存储地址不连续,一般是链式的;速度较慢。
全局区(静态区):全局变量和静态变量等数据是存储在这里的。初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bbs)。程序结束后由操作系统释放。
常量区:常量字符串(.rodata)放在这里,程序结束后,由操作系统进行释放。
代码区:又叫文本段(.text),存放着程序的机器代码,可执行指令就是存储在这里的,这里的代码是只读的。

 引言

C++是一种编程语言,它提供了程序员控制系统内存和资源的能力。在C++编程语言的帮助下,我们可以开发高性能的应用程序。
在C++中,静态是一种方法,我们可以在其中创建变量、对象和函数;然后,我们可以在程序的整个生命周期中为这些函数在编程语言中分配一些空间。
一旦我们可以使用静态的方法或变量,那么就不可能再去修改它。 

1)静态数据成员都是静态存储的,所以必须在main函数之前显示的对其进行初始化;
2)可以在头文件中声明静态全局变量,该头文件被多个cpp文件包含后,包含该头文件的cpp文件实际上会各自拥有独立的同名变量;
3)不能将静态成员函数定义为虚函数;
4)静态成员函数没有this指针;
5)static缩短了子类对父类静态成员访问的时间,相对来说节省了内存空间;
6)如果不想在子类中操作父类的静态成员,则可以在子类中定义一个同名的static成员。这样既可覆盖父类中的静态成员,并且根据C++的多态性变量命名规则,这样做是安全的;
7)静态成员声明在类中,操作在其外部,所以对其取地址操作就跟取普通成员的操作略有不同。静态变量地址是指向其数据类型的指针,函数地址则是一个类型为nonmember的函数指针;

 

static 的用法主要体现在两个方面:面向过程中的 static  和 面向对象中的 static 。
面向过程的    static 主要包括静态全局变量、静态局部变量 和 静态函数。
面向对象的    static 主要包括静态成员变量、静态成员函数、全局静态对象 和 局部静态对象

 

一、面向过程static

1.1、静态变量

经过static修饰的变量,存储在内存的全局静态区。且被static修饰的变量只能在本模块(函数、类、源文件)的所有函数引用。

全局/静态数据区(.data)主要用于存放全局变量和静态变量,在程序启动时,全局/静态数据已经分配了存储空间, 全局/静态数据区中的数据在程序结束后由操作系统释放;
未初始化的静态变量(.bbs)会被程序自动初始化为 0,静态局部变量在程序执行到该对象的声明处时被首次初始化。
在C++中,静态变量的初始化在整个程序启动的时候实现一次。在这里,编译器会保留所有的变量直到程序完成。

静态变量是一种变量类型,它在程序的生命周期内为函数分配空间。在C++中,一旦我们为静态变量分配了空间,它就在程序的生命周期中占据了空间。
即使静态变量被调用无数次,但分配给它的空间是不变的。而在最后一次调用中更新的变量值,将被用于静态函数的下一次调用。

静态变量的语法如下:

static = ;

 

1.1.1  静态全局变量(源文件)

全局变量前添加static关键字,则该变量称为静态全局变量。 

#include 
#include 

static int a = 10;

void Func()
{
    a++;
}

int main()
{
    printf(   "a = %d\n" , a);   //输出:10
    Func();                  //输出:10
    printf(   "a = %d\n" , a);   //输出:11

    system(   "pause" );
    return 0;
}

 

特点:
1)在全局数据中的变量如果没有显示的初始化会自动被程序初始化为0(这个特性非静态全局变量也有),而在函数体内声明的变量如果不显示初始化则会使一个随机值;
2)静态全局变量在声明它的整个文件中都是可见的,而在文件之外是不可见的;
3)静态全局变量在全局数据区分配内存;
4)其他文件中可以定义同名       int 型变量a,不会冲突;

如果将       static 去掉,具有以下特点:
1)全局变量默认是有外部连接性的,其作用域是整个工程,在一个文件内定义的全局变量可以通过包含其所在头文件或显示调用        extern 关键字修饰全局变量的变量名声明来引用;
2)静态全局变量是显示调用       static 修饰的全局变量,其作用域只在声明此变量的文件中,其他文件即使使用       extern 关键字修饰其声明也不可使用;

  

 

1.1.2  静态局部变量(函数)

函数内局部变量前添加static关键字,则该变量称为静态局部变量。

#include 
#include 

void Func()
{
    static int a = 5;
    printf(   "a = %d\n" , a);
    a++;
}

int main()
{
    for (   int i = 0; i < 5; i++)
    {
        Func();     //打印结果:5 6 7 8 9
    }

    system(   "pause" );
    return 0;
}

 

通常,在一个函数作用域内定义一个变量,每次运行到该函数时,系统会给局部变量分配内存。当函数结束时,该变量的内存会被系统回收至栈内存当中。

特点:
1)内存存放在程序的全局数据区中;
2)静态局部变量在程序执行到该对象声明时,会被首次初始化。其后运行到该对象的声明时,不会再次初始化,这也是为什么上面程序测试函数每次输出的值都是递增的原因(只会被初始化一次);
3)如果静态局部变量没有被显式初始化,则其值会自动被系统初始化为0;
4)局部静态变量不能被其作用域之外的其他模块调用,其调用范围仅限于声明该变量的函数作用域当中;

 

1.2 静态函数(源文件)

静态函数的语法如下:

static (){ 
...
}

函数返回类型前添加static关键字,则该变量称为静态函数。

#include 
#include 

static void Func()
{
    printf(   "This is a static function\n" );
}

int main()
{
    Func(); 
 
    system(   "pause" );
    return 0;
}
特点:

1)作用域只在声明它的文件当中,不能被其他文件引用,其他文件可以定义同名的全局函数;
2)其他文件想要调用本文件的静态函数,需要显示的调用extern关键字修饰其声明;

 

二、面向对象static

2.1 静态成员变量(类)

类的成员变量类型之前添加static,此成员变量为静态成员变量。

实例如下:

#include 
#include 
using namespace std;

class Test
{
public :
    Test(   int a,    int b,    int c) : 
        m_a(a),
        m_b(b),
        m_c(c)
    {
        m = a + b + c;
    }

    void Show()
    {
        cout <<    "m = " << m << endl;
    }

private :
    int m_a, m_b, m_c;
    static int m;
};

int Test::m = 0;       //初始化静态数据成员

int main()
{
    Test ClassA(1, 1, 1);
    ClassA.Show();     //输出: 3
    Test ClassB(3, 3, 3);
    ClassB.Show();     //输出: 9
    ClassA.Show();     //输出: 9

    system(   "pause" );
    return 0;
}
特点:
1)静态数据成员的服务对象并非是单个类实例化的对象,而是所有类实例化的对象(这点可以用于设计模式中的单例模式实现);
2)静态数据成员必须显式的初始化分配内存,在其包含类没有任何实例化之前已经有内存分配;
3)静态数据成员与其他成员一样,遵从public,protected,private的访问规则;
4)静态数据成员内存存储在全局数据区,只随着进程的消亡而消亡;

优势:
1)静态数据成员不进入程序全局命名空间,不会与其他全局名称的同名同类型变量冲突;
2)静态数据成员可以实现C++的封装特性,由于其遵守类的访问权限规则,所以相比全局变量更加灵活;

 

2.2 静态成员函数(类)

类的成员函数返回类型之前添加static,此成员函数为静态成员函数。

实例如下:

#include 
#include 
using namespace std;

class Test
{
public :
    Test(   int a,    int b,    int c) : 
        m_a(a),
        m_b(b),
        m_c(c)
    {
        m = a + b + c;
    }

    static void Show()
    {
        cout <<    "m = " << m << endl;
    }

private :
    int m_a, m_b, m_c;
    static int m;
};

int Test::m = 0;    //初始化静态数据成员

int main()
{
    Test ClassA(1, 1, 1);
    ClassA.Show();
    Test ClassB(3, 3, 3);
    ClassB.Show();
    ClassA.Show();

    Test::Show();    //输出: 9

    system(   "pause" ); 
    return 0;
}

 

特点:
1)静态成员函数比普通成员函数多了一种调用方式;
2)在没有实例化的类对象的条件下可以调用类的静态成员函数;
3)静态成员函数中没有隐含的this指针,所以静态成员函数不可以操作类中的非静态成员
(由于第二条可知,类的非静态成员是在类实例化后存在的,而类的成员函数可以在类没有实例化的时候调用,故不能操作类的非静态成员);

 

2.3 静态全局对象(类文件)

全局静态对象在程序执行 main() 之前已经分配好存储空间 ,在 main() 执行完成后进行释放;相当于编译器在 main 函数的显示代码之前和之后分别插入了一段代码用来管理全局对象。

实例如下:

#include "fstream"
#include "string"
using namespace std;

void writeMessageToFile(  const string & msg)
{
  ofstream ofs;
  ofs.open(  "test.txt" , ios::app);
  ofs << msg << endl<

执行结果:

TestClass() 执行

main() 开始执行

main() 结束执行

~TestClass() 执行

2.4 静态局部对象(类方法/函数)

局部静态对象是从其所在函数第一次被调用时进行创建,直到整个程序结束才进行销毁;局部静态对象只有在第一次调用时进行初始化操作。

c++设计一个类只能创建一个实例对象(单例设计模式),实例如下:

#include 

using namespace std;

class Singleton {
private :
    Singleton() { cout <<  "构造函数" << endl; };       // 构造函数是私有的,这样就不能在其它地方创建该实例
    Singleton( const Singleton&) = delete;              // 拷贝构造函数
    Singleton&  operator =( const Singleton&) = delete;  // 阻止赋值
    ~Singleton() { cout <<  "析构函数" << endl; };

public :
    static Singleton& get_instance() {                 // 声明单例的引用
        static Singleton instance_ptr;                 // 局部静态变量
        return instance_ptr;                           // 返回引用
    }
};
int main()
{
    //只有在首次调用 get_instance 时,才会使用构造函数初始化(唯一)局部静态对象实例 ,//局部静态实例只调用了一次构造函数
    cout <<  "instace1" << endl;
    Singleton& instace1 = Singleton::get_instance();
    cout <<  "instace2" << endl;
    Singleton& instace2 = Singleton::get_instance();
    cout <<  "return" << endl;
    //system("pause");
    return 0;
}
//Refer: https: //blog.csdn.net/qq_36079638/article/details/108278239

执行结果:

 

posted @ 2023-04-19 20:31  suntroop  阅读(207)  评论(0)    收藏  举报