Cherno C++系列笔记5——P21~P23 C++中的静态static

文章目录

  • 1.P21 类外的静态(static)
    • 1.1.static分类
    • 1.2.代码分析
  • 2.P22 类和结构体中的静态(static)
    • 2.1.类中的静态的含义
    • 2.2.类中静态成员变量的访问方法
      • 2.2.1.静态成员变量的定义
      • 2.2.2.静态成员变量的访问
      • 2.2.3.程序示例
    • 2.3.类中静态成员函数无法访问非静态成员变量的解释
  • 3.P23 局部静态(local static)
    • 3.1.函数中的局部静态
    • 3.2.局部静态在单例类中的使用
      • 3.2.1.不使用局部静态创建单例类
      • 3.2.2.使用局部静态创建单例类

1.P21 类外的静态(static)

参考:视频 笔记

1.1.static分类

static基本分为两种,类外的static和类内的static:

  • 类外static:表明这个函数或变量只会在当前文件中被链接,不会被外部文件所链接,防止出现意外的链接错误。
  • 类内static:静态变量表明类的所有实例共享内存,这个变量只有一个实例;静态函数类似,没有类的实例会调用这个成员函数。

1.2.代码分析

  • 在当前翻译单元中声明static变量,编译正常
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第1张图片
    由于关键字static,这个变量s_Variable只会在这个翻译单元内部链接。静态变量或函数意味着,当需要将这些函数或变量与实际定义的符号链接时,连接器不会再这个翻译单元的作用域之外,去寻找那个符号定义。

  • 若不使用静态变量,则会出现链接的错误
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第2张图片
    因为这个s_Variable变量已经在另一个翻译单元(Main.cpp)中定义了,所以我们不能有两个同名的全局变量。

  • 修改Main.cpp中这个变量的实际指向
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第3张图片
    去掉这里的赋值,标识这个变量为extern,这意味着它会在外部翻译单元中寻找s_Variable变量。

  • 重新添加static属性
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第4张图片
    如果重新把Static.cpp里的变量标记为静态,这有点像在类中声明一个私有变量,其余所有的翻译单元都不能看到这个s_Variable变量,链接器在全局作用域下也不会看到。此时编译Main.cpp,会得到一个未解析的外部符号的错误,因为在任何地方都找不到名称为s_Variable的整型变量。

2.P22 类和结构体中的静态(static)

参考:视频 笔记(笔记写的很好很详细,可以好好看看)

2.1.类中的静态的含义

  • 类中定义的静态变量和静态函数,本质上是不属于类的,更像是在类的命名空间下定义的普通变量和函数。
  • 静态变量和静态函数是没有类的实例的,因为非静态的类成员变量和函数背后运行机制都是先拿到类的实例指针(即this指针),然后访问类的实例内部的数据,而静态变量和函数拿不到类的实例对象。
  • 静态成员函数只能访问静态成员变量,而不能访问非静态的成员变量。原因同上,就是因为静态成员函数拿不到类的实例,自然就无法访问类的非静态成员变量。而静态成员变量在类之间是共享内存的,更像是定义在一个名为类名的命名空间下,因此静态成员函数只能拿到静态成员变量。

2.2.类中静态成员变量的访问方法

2.2.1.静态成员变量的定义

  • 静态成员变量是所有实例共享的,但是其只是在类中进行了声明,并未定义或初始化(分配内存),类或者类实例就无法访问静态成员变量,这显然是不对的。所以必须先在类外部定义,也就是分配内存。

  • 静态成员变量在编译时存储在静态存储区,即定义过程应该在编译时完成。因此一定要在类外进行定义,但可以不初始化。

2.2.2.静态成员变量的访问

  • 方法1:通过类的实例来访问(不推荐)
int Entity::x;
int Entity::y;

int main()
{    
	Entity e;
	e.x = 2;
	e.y = 3;
}
  • 方法2:通过类来访问,类似命名空间(推荐)
int Entity::x;
int Entity::y;

int main()
{    
	Entity::x = 2;
	Entity::y = 3;
}

2.2.3.程序示例

#include

struct Entity
{
	static int x, y;   // 类中静态变量的声明,但是还没有定义或初始化

	void Print()
	{
		std::cout << x << "," << y << std::endl;
	}
};

// 在类外对类内的静态变量进行定义,这里可以不初始化,也可以初始化
int Entity::x;
int Entity::y;

int main()
{    
    // 访问方法1:使用类的示例进行访问
	Entity e;
	e.x = 2;
	e.y = 3;
	
	 // 访问方法2:使用类名进行访问,类似命名空间	
	Entity::x = 6;
	Entity::y = 7;
	
	std::cin.get();
}

2.3.类中静态成员函数无法访问非静态成员变量的解释

  • 如果我们让x,y变量是非静态的,这样对Entity类的每个实例都有一个单独的x和y,print方法仍然保持static。但静态方法不能访问非静态变量。
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第5张图片
    因为静态方法没有类实例。本质上在类中写的每一个方法,每一个非静态方法总是获得当前类的一个实例作为参数,这是类在幕后的实际工作方法。它们通过隐藏参数(this指针)发挥作用,静态方法不会得到那个隐藏参数。

  • 非静态成员函数的实际操作,相当于给这个函数传入了一个类的实例,这样就可以识别成员变量
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第6张图片

  • 静态方法与在类外部编写方法相同,相当于此时没有传入的类,这样自然也就不知道类中的成员变量是什么了
    Cherno C++系列笔记5——P21~P23 C++中的静态static_第7张图片

3.P23 局部静态(local static)

参考:视频 笔记(写的很好,可以细看)

  • 我们可以在局部作用域中使用static来声明一个变量。声明一个变量我们需要考虑两种情况,一个是变量的生存期,另一个是变量的作用域。生存期指的是变量实际存在的时间(在内存中存多久),而变量的作用域是指我们可以访问变量的范围。
  • 静态局部(Local static)变量允许我们声明一个变量,它的生存周期基本相当于整个程序的生存期,但是作用范围被限制在作用域内。

3.1.函数中的局部静态

在局部作用域中声明为static,被标记为static的变量i生存周期基本和程序相同,并且它被限制在了Func()中,这样就不用把i定义成全局变量了。
Cherno C++系列笔记5——P21~P23 C++中的静态static_第8张图片

3.2.局部静态在单例类中的使用

3.2.1.不使用局部静态创建单例类

  • 单例类是只存在一个实例的类,我们可以不使用静态局部作用域就创建这个单例类(常见做法)。
  • 首先创建静态的单例实例,这里是一个指针。为了返回引用必须有一个返回Singleton&的Get函数,它是静态的,为了返回类的实例要用解引用符号*。
  • 最后需要在类外对静态成员变量进行声明和初始化,可以把它初始化为空指针。

这样就可以调用Singleton::Get得到一个单例对象了。

#include

class Singleton
{
private:
  	   // 静态成员变量,Singleton类型的指针实例
		static Singleton* s_Instance;  
public:
  	 // 返回Singleton类型的实例的引用,注意这里用了解引用符号*,就是为了得到类,而不是类的指针
		static Singleton& Get() { return *s_Instance; };

		void Hello()
		{
				std::cout << "Hello" << std::endl;
		}
};

// 这里是对类中的静态成员变量进行定义和初始化
// Singleton*声明了静态成员变量的类型,Singleton::s_Instance声明了是哪个类中的哪个静态成员变量,最后nullptr是初始化
Singleton* Singleton::s_Instance = nullptr;

int main()
{
    	// Singleton::Get()就得到一个单例的对象,由于是类似命名空间直接调用类的方法得到的,而不是对象的方法,因此只有一个实例
		Singleton::Get().Hello();

		std::cin.get();
}

3.2.2.使用局部静态创建单例类

如果这里没有static关键字,那么这个单例会在栈上创建,函数作用域结束时就会被销毁。通过添加静态让它的生存期延长到永远。意味着我们每次调用Get,它实际上会构造一个单例实例,接下来的时间只会返回这个已经存在的实例,而不会每次调用Get的时候都重复定义这个实例再返回。(本质上和函数内的local static是一样的道理)

#include

class Singleton
{
public:
	   // 返回Singleton类型的实例的引用
		static Singleton& Get() 
		{ 
		  	    static Singleton instance;   // 这里的static关键字很重要,和函数中的local static是一样的功能
				return instance;
		 };

		void Hello()
		{
				std::cout << "Hello" << std::endl;
		}
};

int main()
{
    	// Singleton::Get()就得到一个单例的对象,由于是类似命名空间直接调用类的方法得到的,而不是对象的方法,因此只有一个实例	
		Singleton::Get().Hello();

		std::cin.get();
}

Cherno C++系列笔记5——P21~P23 C++中的静态static_第9张图片

你可能感兴趣的:(C++,c++)