如何在class中优雅地使用常量以及对static成员的探讨(const)(static)(enum)

如果在class中想要定义常量,直观上是想用这样的方式

class A
{
public:
	const int length = 10;
	char buffer[length];
};

上面这个逻辑在class外是行得通的,也是最常用的。

但是在class内部,上面的代码编译时会报错:error: invalid use of non-static data member 'A::length’
vscode中的报错信息为:非静态成员引用必须与特定对象相对(这个翻译真的是迷,但我没有找不到这句话的英文版本),大致意思是想说:必须使用一个实例化的对象来操作非static的成员。

问题可以这样理解

上面代码的class声明只是表述了:「length是int类型的;在实例化之后是不可修改的;该class实例化后,如果没有用初始化列表给length提供新的初值,则length的初值为10」。

注意,= 10这一部分是在class实例化的时候才有意义的,相当于是初始化列表中的默认一项。在class没有实例化之前,这个length是不存在的,自然也就不能用length去指定数组的长度(根据C++语法,数组的长度必须是在编译时就已知的)。

换个角度看

const的作用是限制了代码不许对length进行修改,这个限制是C++语言层面上的,实际上在汇编层面没有什么办法能够阻止你去修改这些数据。所以说声明了const int length和声明普通的int length对于底层来说没什么区别。换句话说,const是留给编译器来看的。

而且,尽管是const的,但是在构造函数的初始化列表中还是可以修改length的值,毕竟在执行初始化列表阶段,这个对象相当于还不存在。初始化列表执行完,const限制即生效,自此之后length的值就不可变了。所以说C++的class里的const数据,在初始化列表之后才会展现出它的不可变属性。

回顾一下C语言中的const

C中的const变量要求在定义时就完成初始化,之后不允许修改。所以说如果C中某个地方出现了const int length = 10;length的值将永远都为10,这样在任何用到length的地方,都可以在编译阶段就确定这个值,所以是可以用来指定数组长度的。

形式上来说,C种的const类似于一种加强版的#define

那怎么办

在类中将成员数据指定为static const类型,即可实现类似C语言的const常量的作用。

class A
{
public:
	static const int length = 10;
	char buffer[length];
};

相对于第1版错误的代码,这里只是在const int length = 10;前面加了static限定符。

这里的static表明了:「这个数据是独立存在的;它是被各个实例所共享的;不管是否有实例对象创建,它都存在」,再结合之前已有的const限定符,length的属性可以概括为:独立存在且不会改变

综上,length在class A没有进行任何实例化之前,就已经存在了,你可以用A::b来使用它(用sizeof去量一下A,其值为10,也就是说length并没有存储在这个class里)。所以这样的length在编译阶段就是已知的常量了,可以用来作为buffer的长度指定。

使用static成员需要注意的

static成员的使用没什么特别的,当作像C语言中的static变量那样用就是了,大部分情况下static成员都被用来在一些C++的demo中统计class被构造/析构的次数,或者是在笔试题中增加难度。

static成员最麻烦的地方是初始化工作。

根据前面分析的static限定符的含义可知,static成员在class A的第1个实例创建之前就已经存在(且初始化了),所以构造函数的初始化列表中不允许初始化static数据成员,但是,static数据成员是必须要有初值的,C++不会给你个默认值的。具体地:

1、任何形式的static成员都可以在类外进行初始化,类外初始化式不用带着static
2、如果static成员是const整数类型const枚举类型,则可以选择在类声明中初始化

ps:非static成员是不能在类外初始化的,必须在类声明中进行初始化(相当给所有的构造函数提供了默认的初始化列表项目)

回到最初的问题上

我们只是想能有个集中的位置来控制数组的长度,之后可能还会有新的static const常量加入这个团体,成为牵一发而动全身的trigger,也有可能会有某一部分数据依赖已有的常量,总之这种类似#define的操作确实促进了代码的规范化、模块化。

但是,一个比较轻微的问题是,static成员终究是要占用内存的,使用#define的形式定义常量虽然不占用内存,但又会脱离class作用域的限制,如何能够结合这两者的优势呢。

目前能想到的一个较优的方案是——使用「类内枚举」(唤起了被湖南籍室友支配的恐惧)

class A
{
public:
	enum /* 匿名的 */
	{
		LENGTH = 10
	};
	char buffer[LENGTH];
};

这样既限制了LENGTH只能在class A内部使用,同时还不占用内存。
另外,由于这里并不需要声明具体的枚举变量,所以枚举类型可以是匿名的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值