静态成员的详细讲解
静态成员(Static Members)是一种特殊的类成员,它们与类本身而不是与任何具体的对象实例相关联。静态成员可以是静态变量或静态函数,允许不同的对象共享数据和行为。
静态成员的特性
共享性:
所有类的实例(对象)共享同一个静态成员,静态成员在所有对象之间是唯一的,因此在内存中只存在一份。
访问方式:
静态成员可以通过类名直接访问,无需创建类的实例,但是非静态成员函数不能用类名直接访问。静态成员也可以通过对象访问,但这不是推荐的方式,因为它不清晰。
class Point
{
public:
void init()
{
}
static void output()
{
}
};
int main()
{
Point::init(); //出错
Point::output();
}
//错误,不能用类名直接访问非静态成员函数,显示:“Point::init”: 调用非静态成员函数需要一个对象
//把main()改一下如下:
int main()
{
Point pt;
pt.init(); //正确,可以用类对象进行访问
pt.output();
}
生命周期:
静态成员的生命周期与程序运行周期一致。在程序运行期间,它们在静态存储区分配内存。
定义和声明:
静态成员变量需要在类外定义(初始化),即使在类内部声明了它们。
#include <iostream>
using namespace std;
class Point
{
public:
Point()
{
m++;
}
~Point()
{
m--;
}
static void output()
{
cout<<m;
}
private:
static int m;
};
int main()
{
Point pt;
pt.output();
}
出错,这是因为类的静态成员变量在使用前必须先初始化。在main()函数前加上:
int Point::m = 0;
//输出:1
静态成员函数:
静态成员函数只能访问静态成员变量,不能访问非静态成员变量,因为它们无法通过 this
指针访问对象的属性
在类的静态成员函数中使用类的非静态成员:
#include <iostream>
using namespace std;
class Point
{
public:
void init()
{
}
static void output()
{
cout<<n;;
}
private:
int n;
};
int main()
{
Point pt;
pt.output();
}
错误 C2597 对非静态成员“Point::n”的非法引用
出错,因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了。
在类的非静态成员函数中使用类的静态成员;
class Point
{
public:
void init()
{
output();
}
static void output()
{
}
};
int main()
{
Point pt;
pt.output();
}
正确,类的非静态成员函数可以调用用静态成员函数,但反之不能。因为静态成员没有this指针,而非静态成员有this指针(this指针的作用之一:区分对象的成员与其他局部变量(会出个文章详细讲解this指针))
静态成员的作用
共享信息:
静态成员变量用于存储所有类实例共享的信息(如计数器),如记录创建的对象数量,或者共享配置设置。
实现类级别的行为:
静态成员函数可以用于写不依赖于特定对象的类方法,这在需要进行类级别的操作时非常有用。
保持状态:
可以在多个对象间共享状态,提供更高效的数据管理。
示例
#include <iostream>
using namespace std;
class Box {
public:
// 静态成员变量
static int objectCount; // 声明静态成员变量
Box() {
// 每当创建一个 Box 对象时,objectCount 增加
objectCount++;
}
// 静态成员函数
static int getObjectCount() {
return objectCount; // 返回当前对象数量
}
};
// 在类外定义静态成员变量
int Box::objectCount = 0;
int main() {
Box box1; // 创建第一个 Box 对象
Box box2; // 创建第二个 Box 对象
// 通过类名访问静态成员函数
cout << "Total Box objects created: " << Box::getObjectCount() << endl; // 输出: 2
return 0;
}
注意事项
静态成员变量必须初始化:
静态成员变量只能在类外定义和初始化,且只能定义一次。
非静态函数不能直接访问静态成员函数:
非静态成员函数可以访问静态成员数据,但静态成员函数无法访问非静态成员变量或函数,因为静态函数不具有 this
指针。
影响设计:过多使用静态成员可能导致代码难以测试和维护,因为静态成员共享状态可能引入线程安全问题和不可预知的行为。
全局静态变量和静态成员函数
static全局变量与普通的全局变量有什么区别:
普通全局变量
非静态全局变量的作用域是整个源文件,当一个源程序由多个源文件组成时,它在各个源文件中都是有效的;作用域是整个程序。
int globalVar = 10; // 普通全局变量
void functionA() {
std::cout << "Global Var in functionA: " << globalVar << std::endl;
}
void functionB() {
std::cout << "Global Var in functionB: " << globalVar << std::endl;
}
静态全局变量
静态成员变量作用域局限在一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。
static int staticGlobalVar = 20; // 静态全局变量
void functionC() {
std::cout << "Static Global Var in functionC: " << staticGlobalVar << std::endl;
}
链接性
普通全局变量:
普通全局变量具有外部链接性,可以在多个源文件之间共享。其他源文件可以通过 extern
声明来访问它。
// file1.cpp
int globalVar = 10; // 普通全局变量
// file2.cpp
extern int globalVar; // 引用 file1.cpp 的普通全局变量
静态全局变量:
静态全局变量具有内部链接性,仅在定义它的文件中可见。即使其他文件尝试引用它,也会导致链接错误。
存储持续性
普通全局变量:
存储在程序的静态存储区(通常在数据段),生命周期从程序开始到结束,变量的值在程序的整个运行期间保持。
静态全局变量:
同样存储在静态存储区,生命周期和普通全局变量相同,但其作用域仅限于定义它的文件。
示例
// file1.cpp
#include <iostream>
int globalVar = 5; // 普通全局变量
static int staticGlobalVar = 10; // 静态全局变量
void showVariables() {
std::cout << "globalVar: " << globalVar << std::endl; // 可以访问普通全局变量
std::cout << "staticGlobalVar: " << staticGlobalVar << std::endl; // 可以访问静态全局变量
}
// file2.cpp
#include <iostream>
// extern int globalVar; // 可以访问 file1.cpp 中定义的普通全局变量,但不能访问静态全局变量
void anotherFunction() {
// std::cout << "staticGlobalVar: " << staticGlobalVar << std::endl; // 这会导致链接错误
}
static函数和普通函数有什么区别:
静态函数:
- 静态成员函数是类的成员函数,但它不依赖于类的任何实例,可以通过类名直接调用。
- 静态函数只能访问静态成员变量和其他静态成员函数,无法访问非静态成员变量和函数,因为它没有
this
指针。
class Example {
public:
static void staticFunction() {
// 只能访问静态成员
}
};
Example::staticFunction(); // 通过类名调用
普通函数:
- 普通成员函数依赖于一个对象实例,可以通过对象调用。
- 普通函数能够访问所有成员变量(静态和非静态)以及其他成员函数,因为它们通过
this
指针定位到具体对象。
class Example {
private:
int value;
public:
void normalFunction() {
// 可以访问非静态成员 value
}
};
Example obj;
obj.normalFunction(); // 通过对象调用
存储持续性
-
静态函数:
- 静态成员函数的内存分配是在程序运行期间分配的,并在程序结束时释放。
- 静态函数在所有对象之间共享,且在其类生命周期内存在一份。
-
普通函数:
- 普通成员函数的存在与其包含的对象实例紧密相关,必须通过对象实例调用。
对象访问
-
静态函数:
- 由于没有
this
指针,静态成员函数无法访问实例的成员变量和普通成员函数。 - 使用静态函数通常用于执行一些与对象状态无关的操作,或者用于访问和修改静态成员。
- 由于没有
-
普通函数:
- 普通成员函数与具体对象关联,并通过
this
指针确保可以修改对象的状态。
- 普通成员函数与具体对象关联,并通过
适用场景
-
静态函数:
- 用于实现类级别的操作,比如工厂方法、单例模式等。
- 适用于执行与特定对象无关的功能,或者需要共享的状态管理。
-
普通函数:
- 用于定义对象的行为,通常用来操作和修改对象的非静态数据。
- 适合用于需要对象特定状态的功能。
示例
#include <iostream>
using namespace std;
class Example {
private:
static int staticValue;
int instanceValue;
public:
Example(int v) : instanceValue(v) {
staticValue++;
}
// 静态成员函数
static void displayStaticValue() {
cout << "Static Value: " << staticValue << endl;
}
// 普通成员函数
void displayInstanceValue() {
cout << "Instance Value: " << instanceValue << endl;
}
};
// 静态成员变量初始化
int Example::staticValue = 0;
int main() {
Example obj1(10);
Example obj2(20);
Example::displayStaticValue(); // 访问静态函数
obj1.displayInstanceValue(); // 访问普通成员函数
obj2.displayInstanceValue(); // 访问普通成员函数
return 0;
}