草庐IT

C/C++const关键字详解(全网最全)

北冥有鱼丶丶 2023-12-17 原文

目录

1、const修饰普通变量

2、const修饰指针

(1)const修饰p:

(2)const修饰*p:

(3)const修饰p和*p

4、const修饰数组

5、const修饰函数形参

(1)const修饰普通形参变量

(2)const修饰指针形参

(3)const修饰引用形参

6、const 修饰函数返回值

(1)const修饰普通类型的返回值

(2)const修饰指针类型的返回值    

7、const修饰成员变量

8、const修饰成员函数


1、const修饰普通变量

用const修饰普通变量实际上就是定义了一个常量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的。 一般对于const变量名都是全大写的。

TYPE const ValueName = value; 

const TYPE ValueName = value;

如果我们强行修改const定义的常量,就会报错。

 实际上,虽然不能直接对常变量进行修改,但是我们可以通过指针对常变量进行修改:

所以当指针指向const常变量时,为避免通过指针修改const常变量,我们应当相应地用const指针指向const常变量。

const int AMOUNT = 100;
int const *p = &AMOUNT;

用const定义的常量有什么作用呢?

假设我们在代码中经常使用到一个数字100,我们可以定义一个常量,让常量的值100:

const int AMOUNT = 100;

如果我们没定义常量,我们需要将100修改为200,需要将代码出现的所有100依次修改为200,而如果我们定义了常量,我们只需要修改常变量的值为200即可,这样就增加了代码的可维护性。

如果将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义。

extend const int ValueName = value;

2、const修饰指针

(1)const修饰p:

int i = 10;
int* const p = &i;

const修饰指针p表示指针不可修改,即一旦得到了某个变量的地址,不能再指向其它变量:

int i = 10;
int * const p = &i;
p++;//p指针指向下一个元素,错误

虽然指针不可修改,但是可以修改指针所指向的变量的值:

int i = 10;
int * const p = &i;
*p = 26;//没问题

(2)const修饰*p:

int i = 10;
const int * p = &i;

const修饰*p表示不可通过指针修改其所指变量的值:

int i = 10;
const int* p = &i;
*p = 26;//通过指针修改其所指变量的值,错误

虽然不可以通过指针修改指针所指变量的值,但是变量i本身可以做任何变化,如:

int i = 10;
const int* p = &i;
i=26;
i++;

p也可以变:

p = &j;

(3)const修饰p和*p

const int* const p;

const修饰p和*p表示指针不能变,指针所指的变量也不能变。

注意以下的区别:

int i;
const int* p1 = &i;//不能通过指针修改
int const* p2 = &i;//不能通过指针修改
int *const p3 = &i;//指针不能修改
/*判断哪个被const了的标志是const在*的前面还是后面*/

4、const修饰数组

数组变量实际上就是const的指针,所以不能直接赋值:

int a[]
//a--->int* const a;

const修饰数组表明数组的每个元素都是const int,无法修改,所以必须通过初始化进行赋值,否则写出来后就无法进行赋值了。

const int a[]
//a---->const int * const a;

5、const修饰函数形参

(1)const修饰普通形参变量

void function(const int Var);

这表示形参不会发生改变,但实际上这是没有意义的,因为我们通常是为了保证外部的实参数据不发生变化,这里形参实际上是实参的拷贝,实参本来就不会发生变化。

(2)const修饰指针形参

void function(const char* Var);

我们把外部实参的地址赋值给用const修饰的指针形参,这样我们就无法通过指针修改其所指的外部实参,保护了数据的安全性。

但如果是这种const指针形参就毫无意义:

void function(char* const Var);

因为这表示指针形参不会改变,但是我们依然可以通过指针修改传过来的外部实参,无法保证外部数据的安全性。

(3)const修饰引用形参

void function(const Type& Var); //引用参数在函数内不可以改变

参数为引用,将外部实参传递给引用形参,传递的是外部实参本身,无需进行拷贝,增加了效率,同时参数是const引用,无法通过引用修改实参,保证了外部数据的安全性。

注:

如果函数参数是非const的引用/指针,它就不能接收const变量(地址),因为会造成权限的放大,会报错,它只能接收非const变量(地址),而如果函数参数是const的引用/指针,它既可以接收const变量(地址),也可以接收非const变量(地址),这是权限的缩小,没问题,如果函数是const普通变量,那么它可以接收const变量,也可以接收非const变量,因为不会造成权限的放大。

6、const 修饰函数返回值

const修饰函数返回值其实用的并不是很多。

(1)const修饰普通类型的返回值

const int fun1();

这个实际上是毫无意义的,因为返回的实际上是临时变量,临时变量本身就具有常性。

(2)const修饰指针类型的返回值    

第一种情况是const修饰*p:

const int * fun2();
const int *p = fun2();//调用正常
int* p2 = fun2();//调用失败

这里简单分析下第三句代码调用失败的原因,因为fun2()实际上是一个const int*指针,我们无法通过指针修改其所指变量,而把fun2()这个const  int*指针赋值给p2后,p2是非const指针,可以通过指针修改其所指变量,这就造成了指针所指变量的访问权限的放大,因此调用失败。

第二种情况就是const修饰p

int* const fun3();
int * const p = fun3();//调用正常
int *p2 = fun3();//调用正常 

看第三句代码,为什么这种情况,返回的int* const指针可以赋值给非const指针呢?

我们可以把fun3()看作一个int * const指针,尽管该指针不可以修改,但是我们可以通过该指针修改其所指变量,赋值给p2后,我们依然可以通过指针修改其所指变量,对指针所指变量的访问权限没有发生变化,这样的赋值当然没有问题了。

7、const修饰成员变量

const修饰类的成员变量,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。   

 class A

    { 

        …

        const int nValue;//成员常量不能被修改

        …

        A(int x): nValue(x) { };//只能在初始化列表中赋值

     } 

8、const修饰成员函数

将const修饰的“成员函数”称之为const成员函数。this指针的类型是:类类型* const this,即成员函数中,不能修改this指针,但是其所指向对象的成员变量可以被修改,const修饰类成员函数,实际上就是将this限定为:const 类类型 * const this,表明在该成员函数中不能对调用对象的任何成员进行修改,起到保护对象的作用。

格式: 将const关键字放在函数的括号后面

class Date
{
public:
    void Display()const
    {
        cout<<_year<<endl;
    }
    //该成员函数实际上是这样
    /*
    void Display(const Date *const this)
    {
        cout<<this->_year<<endl;
    }*/
private:
    int _year;
    int _month;
    int _day;
};

关于const成员函数,以下几点需要我们注意:

  1. 就像尽可能地将函数形参中的引用和指针声明为const一样,只要成员函数不修改调用对象,就应该将其声明为const,这样既可以避免调用对象被修改,而且普通对象和const对象都可以调用(不会造成读写权限的放大)。
  2. 前面提到过,读写权限只能缩小,不能放大,所以,const对象只能调用const成员函数,不可以调用非const成员函数,因为const对象不可以被修改,如果const对象调用非const成员函数,非const成员函数可以修改调用它的const对象,这是权限的放大,会报错。非const对象可以调用非const成员函数,也可以调用const成员函数,因为这是权限的缩小。
  3. const修饰类的成员函数,则该成员函数不能调用类中任何非const成员函数,因为非const成员函数可能会对const对象造成修改,引起权限的放大。
  4. const成员函数能够访问对象的const成员,而其他成员函数不可以。

以上是鄙人对const关键字作用的一些理解,因水平有限,内容之处难免有所缺陷,欢迎各位批评指正......

·       

·            

有关C/C++const关键字详解(全网最全)的更多相关文章

  1. ruby - Ruby 的 AST 中的 'send' 关键字是什么意思? - 2

    我正在尝试学习Ruby词法分析器和解析器(whitequarkparser)以了解更多有关从Ruby脚本进一步生成机器代码的过程。在解析以下Ruby代码字符串时。defadd(a,b)returna+bendputsadd1,2它导致以下S表达式符号。s(:begin,s(:def,:add,s(:args,s(:arg,:a),s(:arg,:b)),s(:return,s(:send,s(:lvar,:a),:+,s(:lvar,:b)))),s(:send,nil,:puts,s(:send,nil,:add,s(:int,1),s(:int,3))))任何人都可以向我解释生成的

  2. ruby-on-rails - `const_missing' : uninitialized constant (NameError) - 2

    每次我尝试使用“script/runner-eproductionClassName.run”从我的Rails2.2应用程序的lib目录运行任何类时,我都会收到以下错误:/usr/lib/ruby/gems/1.8/gems/rails-2.2.2/lib/commands/runner.rb:47:/usr/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:89:in`const_missing':uninitializedconstantClassName(NameError)

  3. ruby - 为什么 return 关键字会导致我的 'if block' 出现问题? - 2

    下面的代码工作正常:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson)do|key,oldv,newv|ifkey==:aoldvelsifkey==:bnewvelsekeyendendputskerson.inspect但是如果我在“ifblock”中添加return,我会得到一个错误:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson

  4. ruby - 在 Ruby 中跳过额外的关键字参数 - 2

    我定义了一个方法:defmethod(one:1,two:2)[one,two]end当我这样调用它时:methodone:'one',three:'three'我得到:ArgumentError:unknownkeyword:three我不想从散列中一个一个地提取所需的键或排除额外的键。除了像这样定义方法之外,有没有办法规避这种行为:defmethod(one:1,two:2,**other)[one,two,other]end 最佳答案 如果不想写**other中的other,可以省略。defmethod(one:1,two:2

  5. 物联网MQTT协议详解 - 2

    一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su

  6. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  7. jquery - Ruby 1.9.1、Rails 2.3.2 和 jrails 0.4 出现 "rescue in const_missing"错误 - 2

    我最近开始了一个项目,团队决定我们希望使用jQuery而不是Prototype/Scriptaculous来满足我们的javascript需求。我们设置了我们的项目,并开始切换。插件已安装viatheseinstructions,一切都按计划进行。不久之后,当尝试运行“./script/server”时,我们收到以下错误:=>Rails2.3.2applicationstartingonhttp://0.0.0.0:3000/usr/local/lib/ruby/gems/1.9.1/gems/activesupport-2.3.2/lib/active_support/depende

  8. ruby - 是否有可能在 Ruby 中以哈希的形式访问关键字参数? - 2

    我知道我能做到:classParentdefinitialize(args)args.eachdo|k,v|instance_variable_set("@#{k}",v)endendendclassA但我想使用关键字参数来更清楚地说明可以接受哪个散列键方法(并进行验证表明不支持此键)。所以我可以写:classAdefinitialize(param1:3,param2:4)@param1=param1@param2=param2endend但是有没有可能写一些更短的东西而不是@x=x;@y=y;...从传递的关键字参数初始化实例变量?是否可以访问作为哈希传递的关键字参数?

  9. ruby :关键字 "in"是什么意思 - 2

    当我第一次在ruby​​中找到关键字“in”时。我想也许我可以这样做:1英寸(0..10)但看起来我不能那样使用它。然后我在ruby​​-lang.org中搜索它,然后用谷歌搜索它。没有答案!ruby中关键字“in”的含义是什么? 最佳答案 您应该能够执行以下操作:foriin0..10doputsiend您提到的表达式1in(0..10)将不起作用,因为常量(1)不能在一定范围内变化-它是一个常量!您需要在in关键字之前命名一个变量。希望对您有所帮助。参见thispage 关于ruby

  10. ruby - 为什么关键字参数必须作为带有符号键的散列传递,而不是 Ruby 中的字符串键? - 2

    我们不能将关键字参数作为带有字符串键的散列传递,关键字参数仅适用于作为符号键的散列。一个简单的例子:defmy_method(first_name:,last_name:)puts"first_name:#{first_name}|last_name:#{last_name}"endmy_method({last_name:'Sehrawat',first_name:'Manoj'})#=>first_name:Manoj|last_name:Sehrawatmy_method({first_name:'Bob',last_name:'Marley'})#=>first_name:Bo

随机推荐