当前位置: 首页 > article >正文

静态成员的详细讲解

静态成员(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;  
}


http://www.kler.cn/a/401487.html

相关文章:

  • IO技术详解
  • web应用安全和信息泄露预防
  • 自动化运维-检测Linux服务器CPU、内存、负载、IO读写、机房带宽和服务器类型等信息脚本
  • SpringBoot Data Redis连接Redis-Cluster集群
  • 远程jupyter lab的配置
  • 【网页设计】CSS3 进阶(动画篇)
  • Spark RDD 的 combineByKey、cogroup 和 compute 算子的作用
  • 第8章硬件维护-8.2 可维护性和可靠性验收
  • 抽象java入门1.5.3.2——类的进阶(中)
  • 嵌入式C语言
  • 填写工单流程
  • CTF练习4
  • TDSQL 免密码登录
  • Openstack15--块存储服务(Cinder)安装
  • SpringCloud详解
  • 阿里云SSL证书每三个月过期续期方法 —— 使用httpsok工具轻松自动续期
  • 机器学习笔记 // 天气预报、股票价格以及历史轨迹(如摩尔定律)// 时间序列的常见属性
  • 如何在Linux系统实现屏幕旋转?触觉智能RK3568鸿蒙开发板演示
  • JavaSE(十四)——文件操作和IO
  • Jmeter数据库压测之达梦数据库的配置方法
  • Flutter 生成二维码
  • React中 setState 是同步的还是异步的?调和阶段 setState 干了什么?
  • 【图像处理识别】数据集合集!
  • 11.15 HTML
  • TCP、IP协议中,ARP与TCP之详解(TCP, Detailed Explanation of ARP and TCP in IP Protocol)
  • ISP是什么?