前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++:22 再议const的作用(上)

C++:22 再议const的作用(上)

作者头像
用户3479834
发布2021-02-03 15:45:52
8240
发布2021-02-03 15:45:52
举报
文章被收录于专栏:游戏开发司机游戏开发司机

我在C++:18篇里说过const的用法,这里我有必要再提升进阶下const的理解。

因为你可能只知道他是怎么用的,但是他为什么这样用,其他用法呢?

首先回顾下const有什么主要的作用?

(1)可以定义const常量,具有不可变性。

(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。

(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。同宏定义一样,可以做到不变则已,一变都变

(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。

(5) 为函数重载提供了一个参考。

(6) 可以节省空间,避免不必要的内存分配。例如:

#define PI 3.14159 //常量宏

const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ......

double i=Pi; //此时为Pi分配内存,以后不再分配!

double I=PI; //编译期间进行宏替换,分配内存

double j=Pi; //没有内存分配

double J=PI; //再进行宏替换,又一次分配内存!

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

(7) 提高了效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

常量与数组的组合有什么特殊吗? 我们给出下面的代码:

代码语言:javascript
复制

有什么问题吗?对了,编译通不过!为什么呢?

const可以用于集合,但编译器不能把一个集合存放在它的符号表里,所以必须分配内存。在这种情况下,const意味着“不能改变的一块存储”。然而,其值在编译时不能被使用,因为编译器在编译时不需要知道存储的内容。自然,作为数组的大小就不行了。

在函数中声明的形参,在函数被调用时会得到实参的值。但是如果在类中呢?

代码语言:javascript
复制
class Demo{
     const int ci; 
     public:     
     void get_ci()     
     {        
           return ci;     
     }
};

当直接用这个类定义一个对象时,会出错,提示没有初始化const成员变量值 且提示ci是一个只读的变量,const用于类中成员变量时,将类成员变为只读属性(只读:不能出现在“=”的左边,但在类中仍可以用一个指针来修改其值。) 所以不可以直接在类的构造函数中初始化const 的成员。

const成员变量只可以初始化列表中初始化

你再看看下面的例子:

代码语言:javascript
复制
class Demo { 
public: 
A(int i=0):test[2]({1,2}) {}//你认为行吗?
private: 
const int test[2]; 
};

vc6下编译通不过,为什么呢

this指针是不是const类型的?

this指针是一个很重要的概念,那该如何理解她呢?也许这个话题太大了,那我们缩小一些:this指针是个什么类型的?这要看具体情况:如果在非const成员函数中,this指针只是一个类类型的;如果在const成员函数中,this指针是一个const类类型的;如果在volatile成员函数中,this指针就是一个volatile类类型的。怎么理解,卖个关子,我将在下一篇文章解释。

const到底是不是一个重载的参考对象?

先看一下下面的例子:

代码语言:javascript
复制
class A {

......

void f(int i) {......}//一个函数

void f(int i) const {......}//上一个函数的重载

......

};

上面是函数重载是没有问题的了,那么下面的呢?

代码语言:javascript
复制
class A {

......

void f(int i) {......}//一个函数

void f(const int i) {......}//?????

......

};

这个是错误的,编译通不过。那么是不是说明内部参数的const不予重载呢?再看下面的例子:

代码语言:javascript
复制
class A {

......

void f(int& ) {......}//一个函数

void f(const int& ) {......}//?????

......

};

这个程序是正确的,看来上面的结论是错误的。

为什么会这样呢?这要涉及到接口的透明度问题。按值传递时,对用户而言,这是透明的,用户不知道函数对形参做了什么手脚,在这种情况下进行重载是没有意义的,所以规定不能重载!当指针或引用被引入时,用户就会对函数的操作有了一定的了解,不再是透明的了,这时重载是有意义的,所以规定可以重载。

什么情况下为const分配内存?

以下是我想到的可能情况,当然,有的编译器进行了优化,可能不分配内存。

A、作为非静态的类成员时;

B、用于集合时;

C、被取地址时;

D、在main函数体内部通过函数来获得值时;

E、const的 class或struct有用户定义的构造函数、析构函数或基类时;。

F、当const的长度比计算机字长还长时;

G、参数中的const;

H、使用了extern时。

与static搭配会不会有问题?假设有一个类:

代码语言:javascript
复制
class A {

public:

......

static void f() const { ......}

......

};

我们发现编译器会报错,因为在这种情况下static不能够与const共存!为什么呢?因为static没有this指针,但是const修饰this指针,所以...

代码语言:javascript
复制
class A {
public:
A(int i=0):test(i),c('a') { }
private:
char c;
const int test;
};
int main()
{
A a(3);
A* pa=&a;
char* p=(char*)pa;
int* pi=(int*)(p+4);//利用边缘调整
*pi=5;//此处改变了test的值!
return 0;
}

要点: 对于优化做的比较好的编译器,代码const int i = 1;

当后面用到变量i 的时候,编译器会优化掉对i 的存取,而直接使用立即数1

读到这里的粉丝可能觉得我这个人比较变态,总喜欢写一些奇奇怪怪的代码来折腾人,没错,真正的游戏线上环境是复杂而且残酷的,必须得这么折腾才能长记性,最后我再折腾下各位,请看如下代码:

代码语言:javascript
复制
void main() 
{
const int i = 1;
*(const_cast<int*>(&i)) = 2;
cout << *(int*)&i << endl;
cout << i << endl;
}

为什么i和*(int*)&i的结果不一样,二者有什么区别?欢迎留言哈,我将统一解答

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 我在C++:18篇里说过const的用法,这里我有必要再提升进阶下const的理解。
相关产品与服务
云硬盘
云硬盘(Cloud Block Storage,CBS)为您提供用于 CVM 的持久性数据块级存储服务。云硬盘中的数据自动地在可用区内以多副本冗余方式存储,避免数据的单点故障风险,提供高达99.9999999%的数据可靠性。同时提供多种类型及规格,满足稳定低延迟的存储性能要求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档