今天在做一个趋势笔试题的时候。才让我有了系统把constkeyword好好总结一下的冲动,由于这个关键词大大小小好多地方都出现过,出现频率很高,而每次仅仅是简短的把答案看了一下,没有真正将其整个使用方法弄透,立即要找工作了。为避免悲剧再次发生,不得不引起我的重视,这才有了这篇博文。

    首先。一看到constkeyword,我们的第一反应就是定义常量。的确。这没有错。const就是constant的缩写嘛,可是其实他的使用方法远不止这些。

接下来我们一一来解说。

    在C程序中,const的使用方法主要有定义常量、修饰函数參数和修饰函数返回值。

而在C++程序中。它除了上述功能外,还能够修饰函数的定义体,定义类中某个成员函数为恒态函数,即不改变类中的数据成员。对于定义常量的使用方法,这里就不多说了,重点看一下修饰函数參数、修饰函数返回值以及修饰函数的定义体。

    0)修饰变量或指针
int ii=0; 

const int i=0;           //i是常量。i的值不会被改动 

 
const int *p1i=&i;        //指针p1i所指内容是常量。能够不初始化 
int const* p11i = &i; //同上,const在*号左边,作用是一样的。

int  * const p2i=ⅈ    //指针p2i是常量。所指内容可改动 

 
const int * const p3i=&i; //指针p3i是常量。所指内容也是常量 

 
p1i=ⅈ                 //合法 

 
*p2i=100;               //合法

    1)修饰函数參数
    首先假设该參数用于输出,那么不管是採用指针传递还是引用传递,都不能加const修饰。所以const仅仅能用于修饰输入參数。这里又分三种情况:输入參数採用值传递还是指针传递还是引用传递。

    (1)假设採用值传递。因为函数将自己主动产生暂时变量用于复制该參数,该输入參数本来就无需保护,所以不须要加const 修饰。

     比如。对于函数void Func1(int x)。写成void Func1(const int x)一点意义也没有。

同理,对于void Func2(A a)也不须要写成void
Func2(const A a),当中A为用户自己定义的对象类型。

    (2)假设採用指针传递,那么加const能够防止函数体内部对该參数进行改变。起到保护作用。
     比如。如果StringCopy函数定义为:void StringCopy(char *strDest, const char *strSrc)。那么,如果函数体试图改变strSrc的内容,编译器将报错。
    (3)假设採用引用传递。
     首先我们来说一下,为什么要引入引用传递这样的方法。原因是:对于非内部数据类型的參数而言,象void
Func(A a) 这样声明的函数注定效率比較底。由于函数体内将产生A 类型的暂时对象用于复制參数a,而暂时对象的构造、复制、析构过程都将消耗时间。为了提高效率。能够将函数声明改为void
Func(A& a)。这样一来,依据引用传递的定义,仅仅是借用了參数的别名,不须要产生暂时对象。
      可是,这样一来,当函数体中改变了參数a的值后,对应的传递的原始值也会对应改变。所以假设不希望改变原始參数。仅仅须要在前面加上const修饰。这样一来。函数终于定义为void
Func(const A& a)。同理,是否应将void Func(int x) 改写为void
Func(const int &x),以便提高效率?全然没有必要。由于内部数据类型的參数不存在构造、析构的过程,而复制也很快。“值传递”和“引用传递”的效率差点儿相当。
     总结一下const作为函数输入參数的使用方法:
     (1)对于非内部数据类型的输入參数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。

比如将void
Func(A a) 改为void Func(const A &a)。

         (2)对于内部数据类型的输入參数。不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又减少了函数的可理解性。比如void
Func(int x) 不应该改为void Func(const int &x)。


      2)修饰函数的返回值
      依据上面的思路,这里也分三种情况。即值传递、指针传递、引用传递。

      (1)假设函数返回值採用“值传递”方式,因为函数会把返回值拷贝到外部暂时的存储单元中,加const 修饰没有不论什么价值。

     比如,不要把函数int
GetInt(void) 写成const int GetInt(void)。

同理不要把函数A
GetA(void) 写成const A GetA(void),当中A 为用户自己定义的数据类型。

    (2)假设函数返回值採用“指针传递”方式。那么函数返回值(即指针)的内容不能被改动,该返回值仅仅能被赋给加const 修饰的同类型指针。
     比如,定义函数为:const char *GetString(void),那么char *str = GetString()将会出现编译错误。应该写成const char *str
= GetString()。

    (3)假设函数返回值是採用“引用传递”方式,它的意义在于能提供啊效率,而这样的方式使用场合并不多。这个时候,一定要搞清楚函数到底是想返回一个对象的“拷贝”还是仅返回“别名”就能够了,否则程序会出错。

     比如。对于类的重载赋值函数A
& operate = (const A &other),假设不加cons修饰,则定义A a, b, c;(a = b) = c,程序合法。可是假设加上const修饰,即const A
& operate = (const A &other)。则程序会报错。


       3)修饰函数的定义体。
       定义const函数。仅仅须要将constkeyword放在函数声明的尾部。不论什么不会改动类的数据成员的函数都应该声明为const 类型。

假设在编写const 成员函数时,不慎改动了数据成员。或者调用了其他非const 成员函数。编译器将报错。这无疑会提高程序的健壮性。

    比如。下面程序中,类stack 的成员函数GetCount 仅用于计数,从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。
class Stack
{
public:
  void Push(int elem);
  int Pop(void);
  int GetCount(void) const; // const 成员函数
private:
  int m_num;
  int m_data[100];
};
int Stack::GetCount(void) const
{
  ++ m_num; // 编译错误,企图改动数据成员m_num
  Pop(); // 编译错误。企图调用非const 函数
  return m_num;
}

    到这里,constkeyword的解说就结束了。下面是几点使用const的几点规则。
    1) const对象仅仅能訪问const成员函数,而非const对象能够訪问随意的成员函数,包含const成员函数。
    2) const对象的成员是不可改动的,然而const对象通过指针维护的对象却是能够改动的。
    3) const成员函数不能够改动对象的数据,无论对象是否具有const性质.它在编译时,以是否改动成员数据为根据,进行检查。
    4) 然而加上mutable修饰符的数据成员,对于不论什么情况下通过不论什么手段都可改动,自然此时的const成员函数是能够改动它的。

C/C++中constkeyword的更多相关文章

  1. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  2. C++ 中的constkeyword

    为什么使用const?採用符号常量写出的代码更easy维护:指针经常是边读边移动,而不是边写边移动:很多函数參数是仅仅读不写的.const最常见用途是作为数组的界和switch分情况标号(也能够用枚举 ...

  3. 剑指Offer读书笔记(持续更新中)

    (1)定义一个空的类型,里面没有不论什么成员变量和成员函数,对该类型求sizeof,得到的结果是多少? 答案是1.空类型的实例中不包括不论什么信息,本来求sizeof应该是0,可是当我们声明该类型实例 ...

  4. C语言:constkeyword、结构体

    前几节内容的解说,主要是内存地址及指针的分析.这一节解说一下easy混淆的keywordconstant及结构体的知识. 一.constkeyword 1. 字符常量的指针 char const *p ...

  5. mapreduce中一个map多个输入路径

    package duogemap; import java.io.IOException; import java.util.ArrayList; import java.util.List; imp ...

  6. Hadoop 中利用 mapreduce 读写 mysql 数据

    Hadoop 中利用 mapreduce 读写 mysql 数据   有时候我们在项目中会遇到输入结果集很大,但是输出结果很小,比如一些 pv.uv 数据,然后为了实时查询的需求,或者一些 OLAP ...

  7. Python中的多进程与多线程(一)

    一.背景 最近在Azkaban的测试工作中,需要在测试环境下模拟线上的调度场景进行稳定性测试.故而重操python旧业,通过python编写脚本来构造类似线上的调度场景.在脚本编写过程中,碰到这样一个 ...

  8. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  9. Angular杂谈系列1-如何在Angular2中使用jQuery及其插件

    jQuery,让我们对dom的操作更加便捷.由于其易用性和可扩展性,jQuer也迅速风靡全球,各种插件也是目不暇接. 我相信很多人并不能直接远离jQuery去做前端,因为它太好用了,我们以前做的东西大 ...

随机推荐

  1. QSerialPort

    (草稿) qt5提供QSerialPort类,封装了串口的api, 可以用这个类写串口通信程序.

  2. winform 之1---窗体介绍

    窗体是winform开发的基础,需要掌握窗体的创建.属性.调用和窗体传值等等. 1.在项目中添加窗体很简单,在项目上点击右键,选择添加windows窗体即可为项目添加新的窗体. 2.窗体的属性设置,右 ...

  3. IOS Notification 通知中心

    1.     通知中心概述 通知中心实际上是在程序内部提供了消息广播的一种机制.通知中心不能在进程间进行通信.实际上就是一个二传手,把接收到的消息,根据内部的一个消息转发表,来将消息转发给需要的对象. ...

  4. HTML5给我们带来了什么

    websocket 根据维基定义 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,在 WebSocket API 中,浏览器和服 ...

  5. QT序列化操作(比较复杂和完善)

    应用需求: 在网盘开发过程中有这样一个需求,即对文件版本进行控制,即记录文件版本的更替信息,这里说的更替信息仅仅是记录不同时刻的文件变化,即文件的增.删.改.重命名等操作.在每个待监控的目录下都会保存 ...

  6. Android GsonUtils工具类

    有那么一个开源jar包,叫gson 可以很方便的将java中的对象和字符串相互转化,数据传输和处理的时候,用到的可能性很大 https://github.com/google/gson http:// ...

  7. C语言中 移位操作运算

    移位规律: 左移时总是移位和补零.右移时无符号数是移位和补零,此时称为逻辑右移;而有符号数大多数情况下是移位后补最左边的位(也就是补最高有效位),移几位就补几位,此时称为算术右移.(其实跟扩展逻辑一样 ...

  8. How to calculate the undo_retention time

    UNDO_RETENTION The undo_retention is a initialization parameter of the undo tablespace. The initiali ...

  9. 华为OJ:计算两个自然时间相加

    按要求一步步做就好 import java.util.Scanner; public class dateAdd { public static void main(String args[]){ S ...

  10. 【E2LSH源代码分析】p稳定分布LSH算法初探

    上一节,我们分析了LSH算法的通用框架,主要是建立索引结构和查询近似近期邻.这一小节,我们从p稳定分布LSH(p-Stable LSH)入手,逐渐深入学习LSH的精髓,进而灵活应用到解决大规模数据的检 ...