C++基础教程面向对象(学习笔记(12))

静态成员函数

静态成员函数
在上一节关于静态成员变量的课程中,您了解到静态成员变量是属于该类的成员变量,而不是该类的对象。如果静态成员变量是公共的,我们可以使用类名和域名解析运算符直接访问它们。但是如果静态成员变量是私有的呢?请考虑以下示例:

class Something
{
private:
    static int s_value;
 
};
 
int Something::s_value = 1; // 初始化程序,即使s_value是私有的,这也没关系,因为它是一个定义
int main()
{
    // 我们如何访问Something :: s_value?它是私有的
}

在这种情况下,我们无法直接从main()访问Something :: s_value,因为它是私有的。通常我们通过公共成员函数访问私有成员。虽然我们可以创建一个普通的公共成员函数来访问s_value,但我们需要实例化一个类类型的对象来使用该函数!我们可以做得更好。事实证明,我们也可以使函数静态。

与静态成员变量一样,静态成员函数不会附加到任何特定对象。以下是带有静态成员函数访问器的上述示例:

class Something
{
private:
    static int s_value;
public:
    static int getValue() { return s_value; } // 静态成员函数
};
 
int Something::s_value = 1; // 初始化
 
int main()
{
    std::cout << Something::getValue() << '\n';
}

由于静态成员函数未附加到特定对象,因此可以使用类名和域名解析运算符直接调用它们。与静态成员变量一样,它们也可以通过类类型的对象调用,但不建议这样做。

静态成员函数没有 this指针*

静态成员函数有两个有趣的怪癖值得注意。首先,因为静态成员函数没有附加到对象,所以它们没有this指针!当你考虑它时这是有道理的 - this指针总是指向成员函数正在处理的对象。静态成员函数不适用于对象,因此不需要this指针。

其次,静态成员函数可以直接访问其他静态成员(变量或函数),但不能访问非静态成员。这是因为非静态成员必须属于类对象,而静态成员函数没有可以使用的类对象!

另一个例子

静态成员函数也可以在类声明之外定义。这与普通成员函数的工作方式相同。

这是一个例子:

class IDGenerator
{
private:
    static int s_nextID; // 这是静态成员的声明
 
public:
     static int getNextID(); // 这是静态函数的声明
};
 
// 这是类外的静态成员的定义。注意我们这里不使用static关键字。
// 我们将从IDs=1开始
int IDGenerator::s_nextID = 1;
 
// 这是类之外的静态函数的定义。注意我们这里不使用static关键字。
int IDGenerator::getNextID() { return s_nextID++; } 
 
int main()
{
    for (int count=0; count < 5; ++count)
        std::cout << "The next ID is: " << IDGenerator::getNextID() << '\n';
 
    return 0;
}

该程序打印:

扫描二维码关注公众号,回复: 3500286 查看本文章

The next ID is:1
The next ID is:2
The next ID is:3
The next ID is:4
The next ID is:5

注意,因为此类中的所有数据和函数都是静态的,所以我们不需要实例化该类的对象以利用其功能!此类使用静态成员变量来保存要分配的下一个ID的值,并提供静态成员函数以返回该ID并将其递增。

关于静态成员的类的警告

使用静态成员编写类时要小心。虽然这种“纯静态类”(也称为“单稳态”)可能很有用,但它们也带来了一些潜在的缺点。

首先,因为所有静态成员只被实例化一次,所以没有办法拥有纯静态类的多个副本(没有克隆类并重命名它)。例如,如果您需要两个独立的IDGenerator对象,则使用单个纯静态类将无法实现此目的。

其次,在关于全局变量的课程中,您了解到全局变量是危险的,因为任何一段代码都可以更改全局变量的值,最终破坏另一段看似无关的代码。纯静态类也是如此。因为所有成员都属于类(而不是类的对象),并且类声明通常具有全局作用域,所以纯静态类本质上等同于在全局可访问的命名空间中声明函数和全局变量,具有全局变量所有必需条件的缺点。

C ++不支持静态构造函数

如果您可以通过构造函数初始化普通成员变量,那么通过扩展,您应该能够通过静态构造函数初始化静态成员变量。虽然一些现代语言确实支持静态构造函数,但不幸的是,C ++不是其中之一。

如果可以直接初始化静态变量,则不需要构造函数:可以在定义点初始化静态成员变量(即使它是私有的)。我们在上面的IDGenerator示例中执行此操作。这是另一个例子:

class MyClass
{
public:
	static std::vector<char> s_mychars;
};
 
std::vector<char> MyClass::s_mychars = { 'a', 'e', 'i', 'o', 'u' }; // 在定义点初始化静态变量

如果初始化静态成员变量需要执行代码(例如循环),那么有许多不同的,有些迟钝的方法。以下代码介绍了一种更好的方法。但是,它有点棘手,你可能永远不需要它,所以如果你愿意,可以随意跳过本节的其余部分。

class MyClass
{
private:
	static std::vector<char> s_mychars;
 
public:
 
	class _init // 我们正在定义一个名为_init的嵌套类
	{
	public:
		_init() // _init构造函数将初始化我们的静态变量
		{
			s_mychars.push_back('a');
			s_mychars.push_back('e');
			s_mychars.push_back('i');
			s_mychars.push_back('o');
			s_mychars.push_back('u');
		}
	} ;
 
private:
	static _init s_initializer; //我们将使用这个静态对象来确保调用_init构造函数
};
 
std::vector<char> MyClass::s_mychars; // 定义我们的静态成员变量
MyClass::_init MyClass::s_initializer; // 定义我们的静态初始化程序,它将调用_init构造函数,它将初始化s_mychars

当定义静态成员s_initializer时,将调用_init()默认构造函数(因为s_initializer的类型为_init)。我们可以使用此构造函数初始化任何静态成员变量。这个解决方案的好处是所有初始化代码都使用静态成员隐藏在原始类中。

Summary

静态成员函数可用于处理类中的静态成员变量。类的对象不需要调用它们。
可以使用所有静态成员变量和静态函数创建类。但是,这些类本质上相当于在全局可访问的命名空间中声明函数和全局变量,除非您有特别好的理由使用它们,否则通常应该避免使用它们。

猜你喜欢

转载自blog.csdn.net/qq_41879485/article/details/82960217