本文章是作者根据史蒂芬·普拉达所著的《C++ Primer Plus》而整理出的读书笔记,如果您在浏览过程中发现了什么错误,烦请告知。另外,此书由浅入深,非常适合有C语言基础的人学习,感兴趣的朋友可以自行阅读此书籍。
类作用域
在类中定义的名称(如类数据成员和类成员函数名)的作用域都为整个类,作用域为整个类的名称旨在该类中是已知的,在类外是不可知的。因此,可以在不同类中使用相同的类成员名而不会引起冲突。另外,类作用域意味着不能外部直接访问类的成员,公有函数也是如此。要调用公有成员,必须由对象通过.成员运算符调用:
Student st1("00001", "wangdamao", 89, 90 ,99);
st1.display();
在定义成员函数时,必须使用作用域解析运算符:
void Student::display()
{
...
}
在类声明或成员函数定义中,可以使用未修饰的成员名称(未限定名称),就像Student()调用set_total()成员函数那样。构造函数名称在被调用时,才能被识别,因为它的名称与类名相同。在其它情况下,使用类成员名时,必须根据上下文使用直接成员运算符(.)、间接成员运算符(->)或作用域解析运算符(:😃。
如下面的代码:
class Tc
{
private:
int m_item;
public:
Tc(int item = 0) {m_item = item;}
void viewTc() const;
};
void Tc::viewTc() const
{
cout << m_item << endl;
}
...
int main()
{
Tc *ptc = new Tc;
Tc ee = Tc(8);
ee.viewTc();
ptc->viewTc();
...
作用域为类的常量
有时候,我们想在类中定义一个常量(比如数组的长度),该常量对于所有对象都是相同的。我们可能会写出这样的代码:#include <iostream>
using namespace std;
class Bakery
{
private:
const int Months = 12; //错误
double costs[Months];
};
int main()
{
Bakery bk;
return 0;
}
编译器会报错:
** $ g++ test.cpp -o test**
test.cpp:8:18: error: invalid use of non-static data member ‘Bakery::Months’
8 | double costs[Months];
| ^~~~~~
test.cpp:7:15: note: declared here
7 | const int Months = 12;
|
这是因为声明类只是描述了对象的格式,并没有创建对象。因此,在创建对象前,将没有用于存储值的空间。
但有两种方式可以实现解决这个问题。
使用关键字static
如下所示:#include <iostream>
using namespace std;
class Bakery
{
private:
static const int Months = 12;
double costs[Months];
};
int main()
{
Bakery bk;
return 0;
}
这将创建一个名为Months的常量,该常量将与其他静态变量存储在一起(数据区)。而不是存储在对象中。因此,只有一个Months常量。
使用枚举
在类声明中声明的枚举的作用域为整个类,因此可以用枚举为整型常量提供作用域为整个类的符号名称。 如下声明: ``` class Bakery { private: enum {Months = 12}; double costs[Months]; }; ``` 注意,用这种方式声明枚举并不会创建类数据成员。也就是说,所有对象中都不包含枚举。Months只是一个符号名称,在作用域为整个类的代码中遇到它时,编译器将用12来替换它。但这种枚举存在一个小问题,两个枚举定义中的枚举量可能发生冲突。如下声明:
enum egg{small, medium, large};
enum t_shirt{small, medium, large};
此时代码将编译出错:
test.cpp:8:18: error: ‘small’ conflicts with a previous declaration
8 | enum t_shirt{small, medium, large};
| ^~~~~
test.cpp:7:14: note: previous declaration ‘Bakery::egg Bakery::small’
7 | enum egg{small, medium, large};
…
这是因为egg和t_shirt的枚举元素都位于相同的作用域内。为了避免这种问题,C++11提供了一种新的枚举,其枚举量的作用域为类。这种枚举的声明类似于下面这样:
enum class egg{small, medium, large};
enum class t_shirt{small, medium, large};
在使用时,需要使用作用域解析符来使用egg::small或者t_shirt::small。
同时,C++11还提高了作用域内枚举的类型安全,回到Bakery的类:
class Bakery
{
private:
enum class Mon{Months = 12}; //需注意,如果要把枚举量的作用域声明为类,则必须提供枚举名。在这里为Mon
double costs[Mon::Months]; //错误
};
我们发现,通过这种方式创建的枚举变量,不能直接用于表示数组的长度。编译错误如下:
test.cpp:8:23: error: could not convert ‘Bakery::Mon::Months’ from ‘Bakery::Mon’ to ‘long unsigned int’
8 | double costs[Mon::Months];
| ~~^
| |
| Bakery::Mon
test.cpp:8:23: error: size of array ‘costs’ has non-integral type ‘Bakery::Mon’
在有些情况下,常规枚举(如enum {Months=12};)会自动转换为整型,如将其赋给int变量或用于比较表达式时,但作用域内枚举不能隐式地转换为整型。
我们可以进行显式类型转换,将枚举类型转换为整型,来解决这个问题。显式类型转换的方法,也就是在使用枚举量时,根据需要进行转换,如下所示:
enum class Mon{Months = 12};
double costs[(long unsigned int)Mon::Months];
书中还提到了另外一种方式,即在声明enum时,制定底层类型,如下所示:
enum class int: Mon { Months = 12};
double costs[Mon::Months];
但这种语法编译会报错,需要继续探究。