Class Scope [类作用域] and C++11 Scoped Enumerations [C++11 作用域内枚举]
- 1. Class Scope Constants (作用域为类的常量)
- 2. C++11 Scoped Enumerations (C++11 作用域内枚举)
- References
Recall that you can use a variable with global
scope anywhere in the file that contains its definition, whereas a variable with local
scope is local to the block that contains its definition. Function names, too, can have global
scope, but they never have local
scope. C++ classes introduce a new kind of scope: class scope.
可以在全局变量所属文件的任何地方使用它,而局部变量只能在其所属的代码块中使用。函数名称的作用域也可以是全局的,但不能是局部的。C++ 类引入了一种新的作用域:类作用域。
Class scope applies to names defined in a class, such as the names of class data members and class member functions. Items that have class scope are known within the class but not outside the class. Thus, you can use the same class member names in different classes without conflict. Also class scope means you can’t directly access members of a class from the outside world. This is true even for public function members. That is, to invoke a public member function, you have to use an object.
在类中定义的名称 (例如类数据成员名和类成员函数名) 的作用域都为整个类,作用域为整个类的名称只在该类中是已知的,在类外是不可知的。因此,可以在不同类中使用相同的类成员名而不会引起冲突。类作用域意味着不能从外部直接访问类的成员,公有成员函数也是如此。要调用公有成员函数,必须通过对象。
Stock sleeper("Albert", 100, 0.25); // create object
sleeper.Show(); // use object to invoke a member function
show(); // invalid - can't call method directly
Similarly, you have to use the scope-resolution operator when you define member functions (在定义成员函数时,必须使用作用域解析运算符):
void Stock::Update(const double price) {
share_val_ = price;
SetTot();
}
In short, within a class declaration or a member function definition you can use an unadorned member name (the unqualified name). A constructor name is recognized when it is called because its name is the same as the class name. Otherwise, you must use the direct membership operator (.
), the indirect membership operator (->
), or the scope-resolution operator (::
), depending on the context, when you use a class member name.
在类声明或成员函数定义中,可以使用未修饰的成员名称 (未限定的名称)。构造函数名称在被调用时,才能被识别,因为它的名称与类名相同。在其他情况下,使用类成员名时,必须根据上下文使用直接成员运算符 (.
) 、间接成员运算符 (->
) 或作用域解析运算符 (::
) 。
unadorned [ˌʌnəˈdɔːnd]:adj. 朴素的,简朴的,不加装饰的
1. Class Scope Constants (作用域为类的常量)
Sometimes it would be nice to have symbolic constants with class scope. For example, a class declaration might use the literal 30 to specify an array size. Because the constant is the same for all objects, it would be nice to create a single constant shared by all objects.
类声明可能使用字面值 30 来指定数组的长度,由于该常量对于所有对象来说都是相同的,因此创建一个由所有对象共享的常量是个不错的主意。
class Bakery
{
private:
const int Months = 12; // Failure
double costs_[Months];
...
But this won’t work because declaring a class describes what an object looks like but doesn’t create an object. Hence, until you create an object, there’s no place to store a value.
但这是行不通的,因为声明类只是描述了对象的形式,并没有创建对象。因此,在创建对象前,将没有用于存储值的空间。
There are, however, a couple ways to achieve essentially the same desired effect.
有两种方式可以实现这个目标, 并且效果相同。
- First, you can declare an enumeration within a class.
An enumeration given in a class declaration has class scope, so you can use enumerations to provide class scope symbolic names for integer constants.
第一种方式是在类中声明一个枚举。在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。
class Bakery
{
private:
enum {Months = 12};
double costs_[Months];
...
Note that declaring an enumeration in this fashion does not create a class data member. That is, each individual object does not carry an enumeration in it. Rather, Months
is just a symbolic name that the compiler replaces with 30 when it encounters it in code in class scope.
用这种方式声明枚举并不会创建类数据成员。也就是说,所有对象中都不包含枚举。另外,Months
只是一个符号名称,在作用域为整个类的代码中遇到它时,编译器将用 30 来替换它。
Because the Bakery class uses the enumeration merely to create a symbolic constant, with no intent of creating variables of the enumeration type, you needn’t provide an enumeration tag.
由于这里使用枚举只是为了创建符号常量,并不打算创建枚举类型的变量,因此不需要提供枚举名。
- C++ has a second way of defining a constant within a class - using the keyword
static
.
class Bakery
{
private:
static const int Months = 12;
double costs_[Months];
...
This creates a single constant called Months
that is stored with other static variables rather than in an object. Thus, there is only one Months
constant shared by all Bakery
objects.
这将创建一个名为 Months
的常量,该常量将与其他静态变量存储在一起,而不是存储在对象中。因此,只有一个 Months
常量,被所有 Bakery
对象共享。
2. C++11 Scoped Enumerations (C++11 作用域内枚举)
Traditional enumerations have some problems. One is that enumerators from two different enum
definitions can conflict.
传统的枚举存在一些问题,其中之一是两个枚举定义中的枚举量可能发生冲突。
enum Egg {Small, Medium, Large, Jumbo};
enum Shirt {Small, Medium, Large};
This won’t fly because the Egg Small
and the Shirt Small
would both be in the same scope, and the names conflict. C++11 provides a new form of enumeration that avoids this problem by having class scope for its enumerators.
这将无法通过编译,因为 the Egg Small
and the Shirt Small
位于相同的作用域内,它们将发生冲突。为避免这种问题,C++11 提供了一种新枚举,其枚举量的作用域为类。
enum class Egg {Small, Medium, Large, Jumbo};
enum class Shirt {Small, Medium, Large};
Alternatively, you can use the keyword struct
instead of class
. In either case, you now need to use the enum
name to qualify the enumerator.
也可使用关键字 struct
代替 class
。无论使用哪种方式,都需要使用枚举名来限定枚举量。
Egg first = Egg::Large; // the Large enumerator of the Egg enum
Shirt second = Shirt::Large; // the Large enumerator of the Shirt enum
Now that the enumerators have class scope, enumerators from different enum definitions no longer have potential name conflicts.
枚举量的作用域为类后,不同枚举定义中的枚举量就不会发生名称冲突了。
C++11 also tightens up type security for scoped enumerations. Regular enumerations get converted to integer types automatically in some situations, but scoped enumerations have no implicit conversions to integer types.
C++11 还提高了作用域内枚举的类型安全。在有些情况下,常规枚举将自动转换为整型,但作用域内枚举不能隐式地转换为整型。
enum Egg { Small, Medium, Large, Jumbo }; // unscoped
enum class Shirt { Small, Medium, Large }; // scoped
Egg one = Medium; // unscoped
Shirt two = Shirt::Large; // scoped
int king = one; // implicit type conversion for unscoped
int ring = two; // not allowed, no implicit type conversion
if (king < Jumbo) // allowed
{
std::cout << "Jumbo converted to int before comparison.\n";
}
if (king < Shirt::Medium) // not allowed
{
std::cout << "Not allowed: < not defined for scoped enum.\n";
}
But you can do an explicit type conversion if you feel you have to:
int three = int(Shirt::Small); // three set to 0
Enumerations are represented by some underlying integer type, and under C98 that choice was implementation-dependent.Thus, a structure containing an enumeration might be of different sizes on different systems. C++11 removes that dependency for scoped enumerations. By default, the underlying type for C++11 scoped enumerations is int
.
枚举用某种底层整型类型表示,在 C++98 中,如何选择取决于实现,因此包含枚举的结构的长度可能随系统而异。对于作用域内枚举,C++11 消除了这种依赖性。默认情况下,C++11 作用域内枚举的底层类型为 int
。
// underlying type for Pizza is short
enum class : short Pizza {Small, Medium, Large};
The : short
specifies the underlying type to be short
. The underlying type has to be an integer type. Under C++11, you also can use this syntax to indicate the underlying type for an unscoped enumeration, but if you don’t choose the type, the choice the compiler makes is implementation-dependent.
: short
将底层类型指定为 short
。底层类型必须为整型。在 C++11 中,也可使用这种语法来指定常规枚举的底层类型,但如果没有指定,编译器选择的底层类型将随实现而异。
References
[1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/
[2] C++ Primer Plus, 6th Edition, https://www.informit.com/store/c-plus-plus-primer-plus-9780321776402
- Class Scope