Effective C++ 条款03:尽可能使用const

场景一 用于修饰指针

char greeting[] = "Hello";     
char* p = greeting;            // non-const pointer, non-const data
const char* p = greeting;      // non-const pointer, const data
char* const p = greeting;      // const pointer, non-const data
const char* const = greeting;  // const pointer, const data

const在*左边,表示被指物是常量,指针所指向的内容不能修改,但是可以修改指针,让指针指向其他对象。

const在*右边,表示指针自身是常量,指针不能修改,不能指向其他对象,但是当前指向的内容可以修改。

const在*的两侧,表示指针自身是常量,被指物也是常量,指针不能指向其他对象,当前指向的内容也不能改变。

场景二 用于对象前、后

void f1(const Widget* pw);
void f2(Widget const * pw);

两种表达式的效果是一样的,都在*的左边,说明被指物是常量。

场景三 用于对STL迭代器的修饰

std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();   // iter的作用相当于 T* const
*iter = 10;    // 没问题,改变iter所指物
iter++;        // 错误,iter是const
std::vector<int>::const_iterator cIter = vec.begin();  // cIter的作用相当于 const T*
*cIter = 10;  // 错误,*cIter是个const
++cIter;      // 没问题,改变cIter 

场景四 用于返回值

class Rational {...};
const Rational operator* (const Rational& lhs, const Rational& rhs);
Rational a, b, c;
...
(a * b) = c;    // 显然在把返回结果设置为const以后,就不允许这样的操作发生
if (a * b = c)  // 如果设置返回值为const的时候,这种手误的操作也不会发生

场景五 用于成员函数本体

const成员函数
class TextBlock {
public:'''const char& operator[](std::size_t position) const   // operator[] for const对象{return text[position];}char& operator[](std::size_t position){return text[position];]}
private:std::string text;
}
// 调用一
TextBlock tb("Hello");
std::cout << tb[0];     // 调用non-const TextBlock::operator[]
const TextBlock ctb("World");
std::cout << ctb[0];    // 调用const TextBlock::operator[]
// 调用二
void print(const TextBlock& ctb) {std::cout << ctb[0];  // 调用const TextBlock::operator[]...
}
// 调用三
std::cout << tb[0];   //没问题,读一个non-const TextBlock
tb[0] = 'x';          //没问题,写一个non-const TextBlock
std::cout << ctb[0];  //没问题, 读一个const TextBlock
ctb[0] = 'x';         //错误,写一个const TextBlock, 错误的原因在于operator[]的返回值为const

注意以上两个函数的返回值都为&,如果返回值是一个char的话,tb[0]= ‘x’;是无法通过编译的;
那是因为,如果函数的返回类型是个内置类型,那么改动函数返回值从来就不合法。纵使合法,C++以by value返回对象这个事实意味被改动的其实是tb.text[0]的一个副本,不是tb.text[0]自身,那不会是你想要的行为。

场景六 bitwise constness和logical constness

class CTextBlock {
public:...char& operator[](std::size_t position) const     //bitwise const 声明,但其实不适当
private:char* pText;
}
// 调用
const CTextBlock cctb("Hello");    // 声明一个常量对象
char* pc = &cctb[0];               // 调用const operator[]取得一个指针,指向cctb的数据
*pc = 'J';                          // cctb现在有了"Jello"这样的内容
// 说明
// const 修饰函数体,说明函数体内不能修改任何non-static成员变量,在函数题内却是没有修改成员变量,但是最后还是修改成功了,那是因为返回值不是char;bitwise constness的主张是成员变量一个bit都不能修改,以上情况导出所谓的logical constness。
class CTextBlock {
public:...std::size_t length() const;
private:char* pText;std::size_t textLength;       //最近一次计算的文本区块长度bool lengthIsValid;           //目前的长度是否有效
};
// 成员函数实现
std::size_t length() const {if (!lengthIsValid) {textLength = std::strlen(pText);  //错误,在const成员函数内不能赋值给textLength和lengthIsValidlengthValid = true;}return textLength;
}

场景七 mutable用法

// 用mutable(可变的)释放掉non-static成员变量的bitwise constness约束
class CTextBlock {
public:...std::size_t length() const;
private:char* pText;mutable std::size_t textLength;  //这些成员变量可能总是被更改,即使在const成员函数内。mutable bool lengthIsValid;     
};
std::size_t CTextBlock::length() const {if (!lengthIsValid) {textLength = std::strlen(pText);lengthIsValid = true;}return textLength;
}

场景八 const实现non-const成员函数

在const和non-const成员函数中避免重复
class TextBlock {
public:...const char& operator[](std::size_t position) const {...    // 边界检查...    // 日志记录数据 ...    // 检验数据完整性return text[position];}char& operator[](std::size_t position) {...   // 边界检查...   // 日志记录数据...   // 检验数据完整性return text[position];}
private:std::string text;
};
// 可以看到以上有非常严重的代码重复, 改进就是用const operator[] 实现 non-const operator[]
class TextBlock {
public:...const char& operator[](std::size_t position) const {.........return text[position];}char& operator[](std::size_t position) {return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);// const_cast转型是因为返回类型,static_cast转型是为了转成const对象。}
};

总结

  1. 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
  2. 编译器强制实施bitwise constness,但你编写程序时应该使用"概念上的常量性(conceptual constness)"。
  3. 当const和non-const 成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复.

转载于:https://www.cnblogs.com/zhonghuasong/p/7290712.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/400315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wndows系统命令总结

window8系统下 打开运行窗口----------鼠标放到任务栏的windows图标下&#xff0c;右击&#xff0c;弹出菜单中如上图或者 打开运行窗口---------按“WINR”键&#xff0c; cmd-------打开命令窗口 services.msc--------打开服务命令 calc-----------启动计算器 dvdplay-------…

【RK3399Pro学习笔记】三、Debian 9 安装 ROS (Thinker Edge R)

目录配置源设置 Key安装初始化rosdep环境配置测试安装rosinstall卸载平台&#xff1a;华硕 Thinker Edge R 瑞芯微 RK3399Pro 固件版本&#xff1a;Tinker_Edge_R-Debian-Stretch-V1.0.4-20200615 参考资料&#xff1a; RK3399(Debian9 - stretch) 安装 ROS Lunar —— WB893…

s:iterator标签的使用

1.在说明s:iterator标签的使用前&#xff0c;先了解下struts2中的Value Stack。 这里参考了webwork中对Value Stack的描述&#xff0c;由于struts2是在webwork的基础上进行升级的&#xff0c; 因此webwork对于Value Stack的表述同样适用于struts2。在这里不描述Value Stack具…

perl学习(二)

2019独角兽企业重金招聘Python工程师标准>>> 在perl中又两个必须搞清楚&#xff0c;就是标量值和标量变量&#xff0c;列表和数组。 直接量就是数值在perl程序代码中的表现方式&#xff0c;就是直接写在程序里的数据&#xff0c;是标量值。如12&a…

【RK3399Pro学习笔记】四、ROS 创建工作空间与功能包

目录创建工作空间编译工作空间功能包创建功能包编译功能包设置环境变量检查环境变量平台&#xff1a;华硕 Thinker Edge R 瑞芯微 RK3399Pro 固件版本&#xff1a;Tinker_Edge_R-Debian-Stretch-V1.0.4-20200615 记录自【古月居】古月ROS入门21讲 | 一学就会的ROS机器人入门教…

hip-hop初探

啥都不说了&#xff0c;上两张图片先 1、使用hiphop的 2、不使用这玩意的 都是前端部署nginx&#xff0c;转发的后面php的 hhvm的配置文件 /etc/hhvm.hdf 目前结论&#xff1a;facebook的这玩意可能适用于facebook业务&#xff0c;对于我的业务来说反而拖后腿转载于:https://ww…

表达式求值(二叉树方法/C++语言描述)(二)

表达式二叉树节点的数据可能是运算数或运算符&#xff0c;可以使用一个联合体进行存储&#xff1b;同时还需要一个变量来指示存储的是运算数还是运算符&#xff0c;可以采用和栈方法求值中一样的枚举类型TokenType&#xff1a; 1 typedef enum2 {3 BEGIN,4 NUMBER,5 …

python连接mysql

1&#xff09; 安装 sudo apt-get install mysql-serversudo apt-get install python-mysqldb 2&#xff09; 使用 import MySQLdbdef get_db_connector():return MySQLdb.connect(hostlocalhost, userroot, passwdchangme, dbdatabaseName)def set_db_close(conn, cur):conn.…

【RK3399Pro学习笔记】五、ROS与USB摄像头

目录usb_cam方法一安装一些要用的包测试usb摄像头方法二下载usb_cam源码编译测试usb摄像头uvc-camera平台&#xff1a;华硕 Thinker Edge R 瑞芯微 RK3399Pro 固件版本&#xff1a;Tinker_Edge_R-Debian-Stretch-V1.0.4-20200615 参考资料&#xff1a; ROS下usb_cam的安装 ——…

这是我们的第一篇博客----偕行软件

欢迎您光临我们的网站&#xff1a;链接 转载于:https://www.cnblogs.com/udsoft/p/3259366.html

wp insert post 插入文章到数据库

在 wordpress 主题目录下的 index.php 文件中&#xff0c;添加如下代码 <?php get_header(); ?><?php// 创建文章对象 $my_post array(post_title > 我的测试文章,post_content > 这是一个测试文章。,post_status > publish,post_author > 1…

Eclipse相关快捷键

Alt左箭头,右箭头 以在编辑窗口切换标签Alt上下箭头, 以自动选择鼠标所在行,并将其上下移动Ctrlf6 可以弹出菜单,上面列出可以切换的编辑窗口,这样不用鼠标也可切换Ctrlf7 可以在视图之间切换 ,如编辑视图,输出视图,工程视图Ctrlf8 可以在不同的观察视图中切换,就是在java视图,…

MFC编程之创建Ribbon样式的应用程序框架

Ribbon界面就是微软从Office2007開始引入的一种为了使应用程序的功能更加易于发现和使用、降低了点击鼠标的次数的新型界面。从实际效果来看&#xff0c;不仅外观美丽&#xff0c;并且功能直观&#xff0c;用户操作简洁方便。 利用MFC向导创建Ribbon样式的单文档应用程序框架的…

【RK3399Pro学习笔记】六、ROS发布者Publisher的编程实现

目录如何实现一个发布者C创建功能包编写程序配置CMakeLists.txt编译并运行发布者python创建并编写脚本运行平台&#xff1a;华硕 Thinker Edge R 瑞芯微 RK3399Pro 固件版本&#xff1a;Tinker_Edge_R-Debian-Stretch-V1.0.4-20200615 记录自【古月居】古月ROS入门21讲 | 一学…

linux下svn命令大全

1、将文件checkout到本地目录 svn checkout path&#xff08;path是服务器上的目录&#xff09; 例如&#xff1a;svn checkout svn://192.168.1.1/pro/domain 简写&#xff1a;svn co 2、往版本库中添加新的文件 svn add file 例如&#xff1a;svn add test.php(添加test.php)…

PHP 从结果集中取得一行作为关联数组:

<?php // 假定数据库用户名&#xff1a;root&#xff0c;密码&#xff1a;123456&#xff0c;数据库&#xff1a;RUNOOB $conmysqli_connect("localhost","root","123456","RUNOOB"); if (mysqli_connect_errno($con)) { echo…

三星:Android之外,技术为王

作为Android阵营的领军代表&#xff0c;三星手机为行业与消费者带来了深远的影响与改变&#xff0c;以至于一提到三星&#xff0c;几乎所有人的第一印象都是三星手机。事实上&#xff0c;在手机之外的众多产品领域中&#xff0c;三星同样有着惊人的成绩与贡献。三星的显示器产品…

ASP.NET Core 源码学习之 Logging[3]:Logger

上一章&#xff0c;我们介绍了日志的配置&#xff0c;在熟悉了配置之后&#xff0c;自然是要了解一下在应用程序中如何使用&#xff0c;而本章则从最基本的使用开始&#xff0c;逐步去了解去源码。 LoggerFactory 我们可以在构造函数中注入 ILoggerFactory&#xff0c;来创建一…

【RK3399Pro学习笔记】七、ROS订阅者Subscriber的编程实现

目录如何实现一个订阅者C编写程序配置CMakeLists.txt编译并运行发布者python创建并编写脚本运行平台&#xff1a;华硕 Thinker Edge R 瑞芯微 RK3399Pro 固件版本&#xff1a;Tinker_Edge_R-Debian-Stretch-V1.0.4-20200615 记录自【古月居】古月ROS入门21讲 | 一学就会的ROS机…