C++ 静态成员

2025年3月17日 | 阅读 7 分钟

大多数情况下,你设计的类使得该类的任何两个实例都是独立的。也就是说,如果我们有两个对象,一个和两个,对一个的更改不应该以任何方式影响另一个。但是,在某些情况下,我们会希望在类的所有副本之间共享数据。例如,我们希望类的所有副本都能访问单个全局资源,或者也许在创建类的第一个实例时有一些我们想要调用的初始化代码。因此,C++ 提供了 `static` 关键字来创建由该类的所有副本共享的类特定的信息。与 `const` 一样,`static` 也有细微之处,有时会适得其反。因此,本讲义讨论了静态成员函数、它们与 `const` 的关系以及静态数据成员。

在 C++ 中使用 `static` 关键字有两种方式。它们是

  1. 静态数据成员
  2. 静态成员函数

静态数据成员

静态数据成员是使用“static”关键字声明的成员。

当我们声明一个数据成员为 `static` 时,无论是在类内部还是在类外部,它都被称为静态数据成员。即使存在许多类对象,静态数据成员也只有一个副本。它总是初始化为零,因为它的默认值是零。它是所有类对象的共享内存,并保留其值。

语法

换句话说,静态数据成员是类特定的变量,每个类实例共享。如果我们有多个类实例,每个实例都使用该变量的同一个副本,因此对静态数据成员的更改会影响多个对象。声明静态数据成员的语法令人困惑,因为它分两个步骤——**声明和定义**。例如,假设你有以下类定义

从上面可以看出,**myStaticData** 声明了一个带有 `static` 关键字的静态数据成员。但是,**`static int myStaticData`** 这一行并没有创建 **`myStaticData`** 变量。相反,它只是告诉编译器变量 **`myStaticData`** 必须在某个点被声明。因此,要初始化 `myStaticData`,我们需要在程序中添加另一行,看起来像这样

int MyClass::myStaticData = 137;

这里有几点需要注意。首先,在声明变量时,我们必须使用完全限定名 **`MyClass::myStaticData`** 而不是 `myStaticData`。其次,我们在变量声明时不要重复 `static` 关键字——否则,编译器会认为我们正在做一些完全不同的事情。最后,虽然 `myStaticData` 被声明为 `private`,但我们仍然可以在类定义外部声明它。静态数据之所以需要这种分两部分的声明/定义,原因有点技术性。这涉及到编译器在哪里为变量分配存储空间,所以现在,记住静态数据必须单独声明和定义。

类中的数据成员可以声明为 `static`。静态数据成员具有某些特殊的特征。它们是

  1. 为整个类只创建一个该成员的副本,并由所有类对象共享,也称为类数据成员。
  2. 它必须声明为 `private` 数据成员。
  3. 它不仅可以通过对象名访问,还可以通过类名访问。
  4. 当它的类创建第一个对象时,它被初始化为零。

除了声明之外,静态数据成员在几乎所有方面看起来都像普通数据成员。考虑 `MyClass` 的以下名为 `doSomething` 的成员函数

这里的代码看起来都很正常,并且这段代码将正常工作。但请注意,当我们修改 `myStaticData` 时,我们正在修改任何其他 `MyClass` 实例可能正在访问的变量。因此,重要的是要确保我们仅在确定信息不特定于任何类时才使用静态数据。

静态成员函数

如果我们创建一个类成员函数作为静态成员函数,它将只访问静态数据成员。如果我们没有任何类对象,它也可以访问。

成员函数

定义成员函数

成员函数可以在两个地方定义

  1. 在类定义内部,以及
  2. 在类定义外部

类中的成员函数可以声明为 `static`。静态成员函数具有某些特殊的特征,它们是

  1. 静态函数只能访问其他静态成员。
  2. 对象名和类名都可以访问静态成员函数。

在成员函数内部,一个名为 `this` 的特殊变量充当指向当前对象的指针。每当我们访问实例变量时,我们都在访问此指针的实例变量。

C++ 如何知道 `this` 指向哪个值?

答案微妙但重要。假设我们有一个类 `MyClass`,其中有一个名为 `doSomething` 的成员函数,它接受两个整数参数。每当我们使用以下语法在 `MyClass` 对象上调用 `doSomething` 时:`myInstance.doSomething(a, b);`

类内的成员函数

  1. 类内的成员函数可以在 `public`(或)`private` 部分声明。
  2. 在类内定义的成员函数被视为内联函数。
  3. 当成员函数很小时,它们在类内定义;否则,它们应该在类外定义。

成员函数声明的代码

输出

C++ Static Member

说明

在上面的程序中,`getdata()` 和 `display()` 成员函数在类内的 `public` 部分定义。在 `main()` 中,声明了对象 `one`。我们知道对象可以访问类的公共成员。对象 `one` 调用公共成员函数 `getdata()` 并初始化其值,然后调用 `display()` 来显示结果。你正在调用

doSomething(&myInstance, a, b);

其中 `doSomething` 被声明为

void doSomething(MyClass *const this, int a, int b)

对于几乎所有意图和目的,这是一个细微的差别,从你作为程序员的角度来看,应该不太重要。但是,一个 N 参数的成员函数是 N+1 参数的自由函数这一事实会在一些地方引起问题。例如,假设你正在编写一个 `Point` 类,如下所示

如果我们有一个我们想传递给 STL `sort` 算法的 vector,如果我们尝试使用此语法,我们将遇到麻烦

sort(myVector.begin(), myVector.end(), &Point::compareTwoPoints);

问题在于 `sort` 函数需要一个接受两个参数并返回 `bool` 的函数。但是,我们提供了一个有三个参数的函数:两个要比较的点和一个隐式的 **“this”** 指针。因此,上述代码将产生错误。

类外的成员函数

当函数很大时,在类外部定义成员函数。要定义类外的函数,应遵循以下步骤

  1. 函数的原型必须在类内声明。
  2. 类名必须在函数名和返回类型之前,用作用域解析运算符(::)分隔。

语法

以下示例说明了在类外部定义的函数。

输出

C++ Static Member

成员函数的特征

  1. 成员函数与普通函数之间的区别在于,普通函数可以自由调用,而成员函数只能使用同一类的对象。
  2. 同一个函数可以在任意数量的类中使用。这是可能的,因为函数的作用域仅限于其类,并且不能相互重叠。
  3. 公共成员函数可以访问私有数据或私有函数。其他函数没有访问权限。
  4. 成员函数可以在不使用对象或点运算符的情况下相互调用。

下一个主题C++ 静态变量