热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++中的const讲解---《EffectiveC++》

条款三:尽可能使用const为什么我们推荐尽可能使用const呢?因为在预编译文件中定义宏和const效果相似,但是宏定义很麻烦而且超容易出错,更重要的是预编译器可能会很早拿走里面的

条款三:
尽可能使用const

为什么我们推荐尽可能使用const呢?因为在预编译文件中定义宏和const效果相似,但是宏定义很麻烦而且超容易出错,更重要的是预编译器可能会很早拿走里面的东西,导致在编译器执行的时候找不到宏定义的内容,调试错误需要好久,为了避免这种麻烦,我们推荐尽可能的使用const!const作为C语言和C++语言中的一个神技,或多或少都有其独特的脾气,今天让我们探索一下const关键字吧!

条例一:const如果出现在指针左边,指针指向的对象是const对象,如const int* a=&b,也可以写成int const*a=&b,表明b是常量,其实b不一定是常量如果出现在指针号右边,则表示该指针是const,不能指向其他对象,如int const a=&b,注意这里有一个显著问题,就是

int b=10;
const int*a=&b;
b=100;
cout<<*a<

想必大家都知道答案了吧,就是100,为什么呢?说好的a指针指向的对象是常量呢?各位稍安勿躁,条例一指的是在当作为函数参数时候的特性,当不作为函数参数时候,由于b本来就是变量,随时都可以变,而a只是指向b的一个普通指针而已,相当于忽略掉const属性,所以当然可以改变呀!

条例二:const成员函数

由于const那么好用,下面我们讲一下const在类中的运用

class TextBlock{
public:
const char& operator[](std::size_t position) const{
return text[position];
}
//第一个const表明operator指向的是const对象,第二个const表明该重载函数不能对类的成员变量进行修改;
char& operator[](std::size_t position){
return text[position];
}
//这个重载函数只是简单的表示重载运算符,适用于非const对象;
private:
std::string text;
};

从以下代码的运行中,可以观察到,可以观察到第一个operator[]代表const对象,同时成员变量无法被修改,第二个operator[]代表非const变量,同时成员变量可以被修改。、

#include 
#include
using namespace std;
class TextBlock{
public:
TextBlock(string s){
this->text = s;
}
const char& operator[](std::size_t position)const{
cout <<"我为const代言" < return text[position];
}
char& operator[](std::size_t position){
cout <<"我为non-const代言" < return text[position];
}
private:
std::string text;
};
int main(){
const TextBlock tb("hello");
cout <0] < TextBlock tb1("hello");
cout <0] < tb1[0] = 'c';
cout <0] < return 0;
}

同样的,我们可以将string替换为char*表示,代码如下:

class TextBlock{
public:
...
cosnt& operator[](std::size_t position) const{
return pText[position];
}
private:
char* pText;
};
const TextBlock cctb("hello");
char* pc=&cctb[0];
*pc='J';//cctb现在有了"Jello"这样的内容

观察上面的代码,为什么此时cctb[0]的值就可以改了呢?注意operator[]的第二个const指的是不能修改成员变量的值,在这里成员变量的值是pText,pText有没有指向其他地址呀,没有吧,即成员变量的值没有被改变呀,因此肯定可以修改呀!

如果我们想要在不准备修改成员变量的成员函数中想要修改成员变量呢?有些拗口,请看如下代码:

class CTextBlock{
public:
...
std::size_t length() const;
private:
char* pText;
std::size_t textLength;
bool LengthIsValid;
}
std::size_t CTextBlock::length() const{
if(!lengthIsValid){
textLength=std::strlen(pText);
lengthIsValid=true;
}
return textLength;
}

上面代码有木有错呢,当然有错,在const成员函数中修改成员变量,如果我们想要其变为正确的怎么办呢?答案很简单,就是将想要更改的变量声明为mutable即可。

条例三:
在const和non-const成员函数中避免重复

怎么解决呢?C++给出的建议是在non-const成员函数中调用const成员函数,为什么不能烦着来呢?const版本中调用non-const版本,注意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 const_cast<char&>(static_cast<const TextBlock&>(*this)[position]]);
...
}

条例4:顶层const和底层const作为重载函数须知
顶层const不影响传入函数的对象,一个拥有顶层const的形参无法和一个没有拥有顶层const的形参区分开来
Record lookup(Phone);
Record lookup(const Phone);

Record lookUp(Phone*);
Record lookUp(Phone* const);
等由于都是顶层const所以每一组的第一个和第二个都一样,无法区分;

底层const确实可以区分的,如果形参是某种类型的指针或者引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时const是底层的。
Record lookup(Account&);//作用与Account引用
Record lookup(const Account&);//作用于常量引用

Record lookup(Account*);//作用于指向Account的指针
Record lookup(const Account*);//作用于指向常量的指针
为了更清楚地理解,我编写了如下的函数:

void record(int &a){
cout <<"hahaha:" <}
void record(const int&a){
cout <
}
int a = 10;
const int b = 100;
int &a1 = a;
const int &b1 = b;
record(a1);
record(b1);

可以看看运行结果:
这里写图片描述
PS:
突然发现自己一直对顶层const和底层const的理解有误,紧急补充一下哈~
顶层const:指针本身是个常量,即const出现在指针右侧;
底层const:指针所指向的对象是个常量,即const出现在指针左侧;


推荐阅读
  • C++简单单向链表实现
    #include?pch.h#include?创建链表typedef?struct?ListTable?{int?nElement;????链表元素int?nSequence;???节点序号ListTable?* ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文介绍了一个程序,可以输出1000内能被3整除且个位数为6的所有整数。程序使用了循环和条件判断语句来筛选符合条件的整数,并将其输出。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • STL学习笔记--数值算法
    数值算法  C++STL的数值算法(Numericalgorithms)是一组对容器元素进行数值计算的模板函数,包括容器元素求和accumulate、两序列元素的内积inner_pro ... [详细]
  • C++ 类的 this 指针 语法练习5
    #include<iostream>#include<string>usingnamespacestd;定义一个类Studentclass ... [详细]
  • [置顶]        C++类的构造函数与析构函数的调用顺序
    1构造函数的调用顺序[1]构造函数按此顺序执行工作:按声明顺序调用基类和成员构造函数。如果类派生自虚拟基类,则会将对象的虚拟基指针初始化。如果类具有或继承了虚函数,则会将对象的虚函数指针初始化。 ... [详细]
  • Igotthiscode(IknowitsinSpanishIcantranslateifneeded)wheretheygivemethefunctionS ... [详细]
  • 数据结构-图详解(图基本概念、图的存储结构及C++实现)
    本文主要介绍关于数据结构,c++,图论的知识点,对【数据结构-图详解(图基本概念、图的存储结构及C++实现)】和【数据结构图的存储结构代码】有兴趣的朋友可以看下由【NUC_Dodamce】投稿的技术文 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
author-avatar
橄榄村
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有