全局变量及static、const用法

文章详细阐述了C++中的全局变量、静态变量以及const常量的概念和用法。全局变量在整个源文件中有效,可通过extern在其他文件中引用。静态变量限制了作用域,只在其定义的编译单元内有效,且静态全局变量在每个编译单元都有独立的存储。const常量可以与static结合使用,改变其作用域和生命周期。此外,文章还讨论了静态成员变量和静态成员函数的特性,如静态成员函数不含有this指针,不能直接访问非静态成员,但可以用于数据共享。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

全局变量

一、编译单元

在VC或VS上编写完代码,点击编译按钮准备生成exe文件时,编译器做了两步工作:

1.将每个.cpp(.c)和相应的.h文件编译成obj文件;
2.将工程中所有的obj文件进行LINK,生成最终.exe文件。

那么,错误可能在两个地方产生:

1.编译时的错误,这个主要是语法错误;
2.链接时的错误,主要是重复定义变量等。

编译单元指在编译阶段生成的每个obj文件。

一个obj文件就是一个编译单元。
一个.cpp(.c)和它相应的.h文件共同组成了一个编译单元。
一个工程由很多编译单元组成,每个obj文件里包含了变量存储的相对地址等。

二、声明与定义

函数或变量在声明时,并没有给它实际的物理内存空间,它有时候可保证你的程序编译通过;
函数或变量在定义时,它就在内存中有了实际的物理空间。
如果你在编译单元中引用的外部变量没有在整个工程中任何一个地方定义的话,那么即使它在编译时可以通过,在连接时也会报错,因为程序在内存中找不到这个变量。
函数或变量可以声明多次,但定义只能有一次。

三、全局变量的定义与声明(extern)

全局变量在整个源文件的作用域都是有效的,只需要在一个源文件中定义全局变量。

两种使用方式:

1、在其他源文件未包含(引用)定义全局变量的源文件时,用extern关键字再次声明这个全局变量即可。
2、在一个源文件中定义这个全局变量,在头文件中用extern关键字再次声明这个全局变量。其它源文件只需要包含这个头文件就可以直接使用该全局变量了。

注:对于全局函数,默认全局函数前有extern关键字,不用再加,包含头文件即可实现全局调用

引用博客:https://blog.youkuaiyun.com/whocarea/article/details/84887401

四、静态全局变量(static)

【注】:使用了static修饰变量,就不能用extern再修饰变量

static修饰的全局变量的声明与定义同时进行,即当你在头文件中使用static声明了全局变量,同时它也被定义了(被程序自动初始化为0)。
static修饰的全局变量的作用域只能是本身的编译单元(本文件)。但数据不会随着函数的推出而释放空间,只会在程序结束时释放。
在其他编译单元使用它时,只是简单的把其值复制给了其他编译单元,其他编译单元会另外开个内存保存它,在其他编译单元对它的修改并不影响本身在定义时的值。
即在编译单元A使用它和编译单元B使用它时,物理地址不一样,A和B对它所做的修改都不能传递给对方。

【注】:static静态全局变量与类中的static静态变量在此处特性不同
多个地方引用静态全局变量所在的头文件(该头文件只含静态全局变量的情况下),不会出现重定义错误,因为在每个编译单元都对它开辟了额外的空间进行存储

五、全局常量(const)

const单独使用时,其特性与static一样(每个编译单元中地址都不一样,不过因为是常量,也不能修改,所以就没有多大关系)。
const与extern一起使用时,其特性与extern一样。(也不能修改,但编译地址是一样的)

extern const char g_szBuffer[];      // h文件中
const char g_szBuffer[] = "123"; // cpp文件中

引用博客:https://blog.youkuaiyun.com/vict_wang/article/details/92005847

static、const和static const成员变量及成员函数

静态成员的提出是为了解决数据共享的问题。类中的静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏规则,即保证了安全性。
静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。
使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。
静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化。引用静态数据成员时,采用如下格式:
<类名>::<静态成员名>

原文链接:https://www.runoob.com/w3cnote/cpp-static-const.html

一、static、const成员变量

1、static、const成员变量

const 定义的常量在超出其作用域之后其空间会被释放,而 static 定义的静态常量在函数执行后不会释放其存储空间。

static 表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。
一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。
在 C++ 中,static 静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件(cpp文件)中初始化。

const 数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其 const 数据成员的值可以不同。
所以不能在类的声明中初始化 const 数据成员,因为类的对象没被创建时,编译器不知道 const 数据成员的值是什么。
(要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static cosnt。)
在 C++ 中,const 成员变量也不能在类定义处初始化,只能在类的构造函数初始化列表进行,并且必须有构造函数。
eg.

class Test{
public:
    Test():a(0){}
    enum {size1=100,size2=200};
private:
    const int a;//只能在构造函数初始化列表中初始化
    static int b;//在类的实现文件中定义并初始化
    conststatic int c;//与 static const int c;相同。
};
 
int Test::b=0;//static成员变量不能在构造函数初始化列表中初始化,因为它不属于某个对象。
cosnt intTest::c=0;//注意:给静态成员变量赋值时,不需要加static修饰符,但要加cosnt。

2、static、const成员函数
cosnt 成员函数主要目的是防止成员函数修改对象的内容。即 const 成员函数不能修改成员变量的值,但可以访问成员变量。
static 成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员函数没有 this 指针,这导致:

1、不能直接存取类的非静态成员变量,调用非静态成员函数。
2、不能被声明为 virtual。

【注】:在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。

二、静态成员变量初始化问题

1、const成员变量初始化(引用型成员变脸初始化是同样方式)

class foo{
public:
    foo():i(100){} //right
private:
    const int i=100;//error!!!
};
foo::foo():i(100){}//或者通过这样的方式来进行初始化

2、static成员变量初始化

类中的 static 变量是属于类的,不属于某个对象,它在整个程序的运行过程中只有一个副本,因此不能在定义对象时对变量进行初始化
静态成员不能在类的定义里初始化(除int外)。静态数据成员实际上是类域中的全局变量,因此不应该在头文件里初始化

【注】:不要试图在头文件中定义(初始化)静态数据成员。在大多数的情况下,这样做会引起重复定义这样的错误。即使加上#ifndef #define #endif 或者 #pragma once 也不行!

eg.

//xxx.h文件 
class base{ 
private: 
    static const int _i;//声明,标准c++支持有序类型在类体中初始化,但vc6不支持。 
}; 

//xxx.cpp文件 
const int base::_i=10;//定义(初始化)时不受private和protected访问限制.

注意:
(1)初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆(全局静态变量如何初始化?)
(2)初始化时不加该成员的访问权限控制符private、public等
(3)初始化时使用作用域运算符来表明它所属的类,因此,静态数据成员是类的成员而不是对象的成员。

特点:
(1)静态数据成员被类的所有对象所共享,包括该类派生类的对象。即派生类对象与基类对象共享基类的静态数据成员。
(2)静态数据成员可以成为成员函数的默认参数,而普通数据成员则不可以。
(3)静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为所属类类型的指针或引用。

3、static cosnt 和 const static 成员变量初始化

class Test{
public:
    static const int mask1;
    conststatic int mask2;
};
constTest::mask1=0xffff;
constTest::mask2=0xffff;

它们的初始化没有区别,虽然一个是静态常量一个是常量静态。静态都将存储在全局变量区域,其实最后结果都一样。可能在不同编译器内,不同处理,但最后结果都一样。

三、静态成员函数

在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员。如果静态成员函数中要引用非静态成员时,可通过对象来引用。
eg.

class M{
public:
    M(int a){
        A=a;
        B+=a;
    }
    
    static void f1(M m);
private:
    int A;
    static int B;
};
 
void M::f1(M m)
{
    cout<<"A="<<m.a<<endl;
    cout<<"B="<<b<<endl;
}
 
int M::B=0;
 
void main()
{
    M P(5),Q(10);
    M::f1(P); //调用时不用对象名
    M::f1(Q);
}

特点:
(1)静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用类成员函数指针来储存。

eg.

class base{ 
    static int func1(); 
    int func2(); 
}; 
int (*pf1)()=&base::func1;//普通的函数指针 
int (base::*pf2)()=&base::func2;//成员函数指针

(2)静态成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。
(3)静态成员函数不可以同时声明为 virtual、const、volatile函数。

eg.

class base{ 
    virtual static void func1();//错误 
    static void func2() const;//错误 
    static void func3() volatile;//错误 
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值