第10章 对象和类——对象和类(五)类作用域

本文主要介绍了C++中类的作用域规则,如何通过对象调用公有成员,以及处理类常量和枚举的技巧,包括静态常量、作用域内枚举和类型安全问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文章是作者根据史蒂芬·普拉达所著的《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];

但这种语法编译会报错,需要继续探究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值