Team LiB
Previous Section Next Section

12.6. static Class Members

12.6. static 类成员

It is sometimes necessary for all the objects of a particular class type to access a global object. Perhaps a count is needed of how many objects of a particular class type have been created at any one point in the program, or the global object may be a pointer to an error-handling routine for the class, or it may be a pointer to the free-store memory for objects of this class type.

对于特定类类型的全体对象而言,访问一个全局对象有时是必要的。也许,在程序的任意点需要统计已创建的特定类类型对象的数量;或者,全局对象可能是指向类的错误处理例程的一个指针;或者,它是指向类类型对象的内在自由存储区的一个指针。

However, making the object global violates encapsulation: The object exists to support the implementation of a particular class abstraction. If the object is global, general user code can modify the value. Rather than defining a generally accessible global object, a class can define a class static member.

然而,全局对象会破坏封装:对象需要支持特定类抽象的实现。如果对象是全局的,一般的用户代码就可以修改这个值。类可以定义 静态成员,而不是定义一个可普遍访问的全局对象。

Ordinary, nonstatic data members exist in each object of the class type. Unlike ordinary data members, a static data member exists independently of any object of its class; each static data member is an object associated with the class, not with the objects of that class.

通常,非 static 数据成员存在于类类型的每个对象中。不像普通的数据成员,static 数据成员独立于该类的任意对象而存在;每个 static 数据成员是与类关联的对象,并不与该类的对象相关联。

Just as a class may define shared static data members, it may also define static member functions. A static member function has no this parameter. It may directly access the static members of its class but may not directly use the nonstatic members.

正如类可以定义共享的 static 数据成员一样,类也可以定义 static 成员函数。static 成员函数没有 this 形参,它可以直接访问所属类的 static 成员,但不能直接使用非 static 成员。

Advantages of Using Class static Members

使用类的 static 成员的优点

There are three advantages to using static members rather than globals:

使用 static 成员而不是全局对象有三个优点。

  1. The name of a static member is in the scope of the class, thereby avoiding name collisions with members of other classes or global objects.

    static 成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突。

  2. Encapsulation can be enforced. A static member can be a private member; a global object cannot.

    可以实施封装。static 成员可以是私有成员,而全局对象不可以。

  3. It is easy to see by reading the program that a static member is associated with a particular class. This visibility clarifies the programmer's intentions.

    通过阅读程序容易看出 static 成员是与特定类关联的。这种可见性可清晰地显示程序员的意图。

Defining static Members

定义 static 成员

Amember ismade static by prefixing the member declaration with the keyword static. The static members obey the normal public/private access rules.

在成员声明前加上关键字 static 将成员设为 staticstatic 成员遵循正常的公有/私有访问规则。

As an example, consider a simple class intended to represent a bank account. Each account has a balance and an owner. Each account earns interest monthly, but the interest rate applied to each account is always the same. We could write this class as

例如,考虑一个简单的表示银行账户的类。每个账户具有余额和拥有者,并且按月获得利息,但应用于每个账户的利率总是相同的。可以按下面的这样编写这个类

     class Account {
     public:
         // interface functions here
         void applyint() { amount += amount * interestRate; }
         static double rate() { return interestRate; }
         static void rate(double); // sets a new rate
     private:
         std::string owner;
         double amount;
         static double interestRate;
         static double initRate();
     };

Each object of this class has two data members: owner and amount. Objects do not have data members that correspond to static data members. Instead, there is a single interestRate object that is shared by all objects of type Account.

这个类的每个对象具有两个数据成员:owneramount。对象没有与 static 数据成员对应的数据成员,但是,存在一个单独的 interestRate 对象,由 Account 类型的全体对象共享。

Using a Class static Member

使用类的 static 成员

A static member can be invoked directly from the class using the scope operator or indirectly through an object, reference, or pointer to an object of its class type.

可以通过作用域操作符从类直接调用 static 成员,或者通过对象、引用或指向该类类型对象的指针间接调用。

     Account ac1;
     Account *ac2 = &ac1;
     // equivalent ways to call the static member rate function
     double rate;
     rate = ac1.rate();        // through an Account object or reference
     rate = ac2->rate();       // through a pointer to an Account object
     rate = Account::rate();   // directly from the class using the scope operator

As with other members, a class member function can refer to a class static member without the use of the scope operator:

像使用其他成员一样,类成员函数可以不用作用域操作符来引用类的 static 成员:

     class Account {
     public:
          // interface functions here
          void applyint() { amount += amount * interestRate; }
     };

Exercises Section 12.6

Exercise 12.36:

What is a static class member? What are the advantages of static members? How do they differ from ordinary members?

什么是 static 类成员?static 成员的优点是什么?它们与普通有什么不同?

Exercise 12.37:

Write your own version of the Account class.

编写自己的 Account 类版本。


12.6.1. static Member Functions

12.6.1. static 成员函数

Our Account class has two static member functions named rate, one of which was defined inside the class. When we define a static member outside the class, we do not respecify the static keyword. The keyword appears only with the declaration inside the class body:

Account 类有两个名为 ratestatic 成员函数,其中一个定义在类的内部。当我们在类的外部定义 static 成员时,无须重复指定 static 保留字,该保留字只出现在类定义体内部的声明处:

     void Account::rate(double newRate)
     {
         interestRate = newRate;
     }

static Functions Have No this Pointer
static 函数没有 this 指针

A static member is part of its class but not part of any object. Hence, a static member function does not have a this pointer. Referring to this either explicitly or implicitly by using a nonstatic member is a compile-time error.

static 成员是类的组成部分但不是任何对象的组成部分,因此,static 成员函数没有 this 指针。通过使用非 static 成员显式或隐式地引用 this 是一个编译时错误。

Because a static member is not part of any object, static member functions may not be declared as const. After all, declaring a member function as const is a promise not to modify the object of which the function is a member. Finally, static member functions may also not be declared as virtual. We'll learn about virtual functions in Section 15.2.4 (p. 566).

因为 static 成员不是任何对象的组成部分,所以 static 成员函数不能被声明为 const。毕竟,将成员函数声明为 const 就是承诺不会修改该函数所属的对象。最后,static 成员函数也不能被声明为虚函数。我们将在第 15.2.4 节学习虚函数。

Exercises Section 12.6.1

Exercise 12.38:

Define a class named Foo that has a single data member of type int. Give the class a constructor that takes an int value and initializes the data member from that value. Give it a function that returns the value of its data member.

定义一个命名为 Foo 的类,具有单个 int 型数据成员。为该类定义一个构造函数,接受一个 int 值并用该值初始化数据成员。为该类定义一个函数,返回其数据成员的值。

Exercise 12.39:

Given the class Foo defined in the previous exercise, define another class Bar with two static data elements: one of type int and another of type Foo.

给定上题中定义的 Foo 类定义另一个 Bar 类。Bar 类具有两个 static 数据成员:一个为 int 型,另一个为 Foo 类型。

Exercise 12.40:

Using the classes from the previous two exercises, add a pair of static member functions to class Bar. The first static, named FooVal, should return the value of class Bar's static member of type Foo. The second member, named callsFooVal, should keep a count of how many times xval is called.

使用上面两题中定义的类,给 Bar 类增加一对成员函数:第一个成品命名为 FooVal,返回 Bar 类的 Foo 类型 static 成员的值;第二个成员命名为 callsFooVal,保存 xval 被调用的次数。


12.6.2. static Data Members

12.6.2. static 数据成员

static data members can be declared to be of any type. They can be consts, references, arrays, class types, and so forth.

static 数据成员可以声明为任意类型,可以是常量、引用、数组、类类型,等等。

static data members must be defined (exactly once) outside the class body. Unlike ordinary data members, static members are not initialized through the class constructor(s) and instead should be initialized when they are defined.

static 数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static 成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。

The best way to ensure that the object is defined exactly once is to put the definition of static data members in the same file that contains the definitions of the class noninline member functions.

保证对象正好定义一次的最好办法,就是将 static 数据成员的定义放在包含类非内联成员函数定义的文件中。



static data members are defined in the same way that other class members and other variables are defined. The member is defined by naming its type followed by the fully qualified name of the member.

定义 static 数据成员的方式与定义其他类成员和变量的方式相同:先指定类型名,接着是成员的完全限定名。

We might define interestRate as follows:

可以定义如下 interestRate

     // define and initialize static class member
     double Account::interestRate = initRate();

This statement defines the static object named interestRate that is a member of class Account and has type double. Like other member definitions, the definition of a static member is in class scope once the member name is seen. As a result, we can use the static member function named initRate directly without qualification as the initializer for rate. Note that even though initRate is private, we can use this function to initialize interestRate. The definition of interestRate, like any other member definition, is in the scope of the class and hence has access to the private members of the class.

这个语句定义名为 interestRatestatic 对象,它是类 Account 的成员,为 double 型。像其他成员定义一样,一旦成员名出现,static 成员的就是在类作用域中。因此,我们可以没有限定地直接使用名为 initRatestatic 成员函数,作为 interestRate 初始化式。注意,尽管 initRate 是私有的,我们仍然可以使用该函数来初始化 interestRate。像任意的其他成员定义一样,interestRate 的定义是在类的作用域中,因此可以访问该类的私有成员。

As with any class member, when we refer to a class static member outside the class body, we must specify the class in which the member is defined. The static keyword, however, is used only on the declaration inside the class body. Definitions are not labeled static.

像使用任意的类成员一样,在类定义体外部引用类的 static 成员时,必须指定成员是在哪个类中定义的。然而,static 关键字只能用于类定义体内部的声明中,定义不能标示为 static



Integral const static Members Are Special
特殊的整型 const static 成员

Ordinarily, class static members, like ordinary data members, cannot be initialized in the class body. Instead, static data members are normally initialized when they are defined.

一般而言,类的 static 成员,像普通数据成员一样,不能在类的定义体中初始化。相反,static 数据成员通常在定义时才初始化。

One exception to this rule is that a const static data member of integral type can be initialized within the class body as long as the initializer is a constant expression:

这个规则的一个例外是,只要初始化式是一个常量表达式,整型 const static 数据成员就可以在类的定义体中进行初始化:

     class Account {
     public:
         static double rate() { return interestRate; }
         static void rate(double);  // sets a new rate
     private:
         static const int period = 30; // interest posted every 30 days
         double daily_tbl[period]; // ok: period is constant expression
     };

A const static data member of integral type initialized with a constant value is a constant expression. As such, it can be used where a constant expression is required, such as to specify the dimension for the array member daily_tbl.

用常量值初始化的整型 const static 数据成员是一个常量表达式。同样地,它可以用在任何需要常量表达式的地方,例如指定数组成员 daily_tbl 的维。

When a const static data member is initialized in the class body, the data member must still be defined outside the class definition.

const static 数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义。



When an initializer is provided inside the class, the definition of the member must not specify an initial value:

在类内部提供初始化式时,成员的定义不必再指定初始值:

     // definition of static member with no initializer;
     // the initial value is specified inside the class definition
     const int Account::period;

static Members Are Not Part of Class Objects
static 成员不是类对象的组成部分

Ordinary members are part of each object of the given class. static members exist independently of any object and are not part of objects of the class type. Because static data members are not part of any object, they can be used in ways that would be illegal for nonstatic data members.

普通成员都是给定类的每个对象的组成部分。static 成员独立于任何对象而存在,不是类类型对象的组成部分。因为 static 数据成员不是任何对象的组成部分,所以它们的使用方式对于非 static 数据成员而言是不合法的。

As an example, the type of a static data member can be the class type of which it is a member. A nonstatic data member is restricted to being declared as a pointer or a reference to an object of its class:

例如,static 数据成员的类型可以是该成员所属的类类型。非 static 成员被限定声明为其自身类对象的指针或引用:

     class Bar {
     public:
         // ...
     private:
         static Bar mem1; // ok
         Bar *mem2;       // ok
         Bar mem3;        // error
     };

Similarly, a static data member can be used as a default argument:

类似地,static 数据成员可用作默认实参:

     class Screen {
     public:
         // bkground refers to the static member
         // declared later in the class definition
         Screen& clear(char = bkground);
     private:
         static const char bkground = '#';
     };

A nonstatic data member may not be used as a default argument because its value cannot be used independently of the object of which it is a part. Using a nonstatic data member as a default argument provides no object from which to obtain the member's value and so is an error.

static 数据成员不能用作默认实参,因为它的值不能独立于所属的对象而使用。使用非 static 数据成员作默认实参,将无法提供对象以获取该成员的值,因而是错误的。

Exercises Section 12.6.2

Exercise 12.41:

Given the classes Foo and Bar that you wrote for the exercises to Section 12.6.1 (p. 470), initialize the static members of Foo. Initialize the int member to 20 and the Foo member to 0.

利用第 12.6.1 节的习题中编写的类 FooBar,初始化 Foostatic 成员。将 int 成员初始化为 20,并将 Foo 成员初始化为 0。

Exercise 12.42:

Which, if any, of the following static data member declarations and definitions are errors? Explain why.

下面的 static 数据成员声明和定义中哪些是错误的(如果有的话)?解释为什么。

     // example.h
     class Example {
     public:
         static double rate = 6.5;

         static const int vecSize = 20;
         static vector<double> vec(vecSize);
     };

     // example.C
     #include "example.h"
     double Example::rate;
     vector<double> Example::vec;


Team LiB
Previous Section Next Section