zoukankan      html  css  js  c++  java
  • [Effective C++ 003]尽可能使用const

    三、尽可能使用const
    经典面试题:     

        ①说出const的至少3个作用
        ②const char* p = x;
           char* const p = x;
          说出上处两个const的作用。

    说到这,其实最好是说一下const的语义:

    1、const语义
    1).代替#define
    2).使某个对象(变量)、值,指针,引用不能被修改
    3).使类的静态对象在类内部可以初始化
    当我们要在类的内部定义某个常量时,就可能用const来修饰,否则的话,就不能初始化

    1 class print {
    2 private:
    3 static const int count = 10; //不能写成static int count = 10;这里的类型只能用整形
    4 string info[count];
    5 };
    6  const int print::count;(附录1)

    4).修饰函数参数和返回值,对于类的成员函数可以确定不能修改类的数据成员.
    ①在函数体的末尾和前面加const的区别
    ②令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性。
    如:

    1 const cgi_ChangeVolumeOperationListData& GetData() const {
    2 return *_pData;
    3 }

    注:如果const被指物是常量,那么将其写在类型之后,星号之前的意义是一样的,如:
    void f1(const Func* p); // f1获得一个指针,指向一个常量的(Func)对象
    void f2(Func const * p);
    5).用const来修饰重载的类的成员函数
    详见下面第3点

    可以看看这个函数中所有const的意义:
    const char* const foo(char const * const str) const
    第一个const表示返回类型为const,也就是不能把此函数的返回值当作左值来使用。
    第二个const表示指针的不可变性,但在这是可以省略,因为返类型已经是const。
    第三个const表示str的常量性,也就其内容是不能改变,可以写在其前面的char的前面。
    第四个const表示str的指针的常量性,也就是此指针不能指向别的地址。
    第五个const表示此函数的常量性(前提是类的成员函数),不能修改所在类的数据成员

    2、STL迭代器与const的关系

    所谓的STL,其实就是

    STL:Standard Template Library,标准模板库
    STL提供了一组容器、迭代器、函数对象和算法的模板。容器是一个类似数组的单元,其存储的值类型相同;
    算法是完成特定处理的处方;迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针。

    2)声名STL迭代器为const
    STL迭代器是指针为根据塑模出来的,其作用就像一个T*指针,因此声名迭代器为const就跟声名指针
    一样的,表示这个迭代器不得指向不同的东西,但其指向的事物的值却是可以修改的。要使其指向值不可被修
    改,可以定义为const_iterator,例:

    1 std::vector<int> vec;
    2 ...
    3 const std::vector<int>::iterator iter = vec.begin(); // iter的作用就如T* const
    4 *iter = 10; // 此时iter是const,是不可更改
    5 
    6 std::vector<int>::const_iterator iter = vec.begin();
    7 ++iter; // 此时*iter是const,不可更改

    3、const成员函数
    1)将const实施与成员函数的目的,是为了确认该成员函数可作用与const对象身上,其理由如下:
    ①使得类的接口比较容易被理解
    ②使“操作const对象”成为可能

    2)在成员函数中,如果只是常量性不同,是可以被重载的,如:

     1 class string {
     2 public:
     3 char& operator[](int position){ //
     4 return data[position];
     5 }
     6 const char& operator[](int position) const {
     7 return data[position];
     8 }
     9 private:
    10 char *data;
    11 };

    那么我们就可以如下使用:
    string s1 = "hello";
    cout << s1[0]; // 调用非const
    const string s2 = "world";
    cout << s2[0]; // 调用const
    ★非const operator[]的返回类型必须是一个char的引用———char本身则不行,因为修改一个“返回值为固定
    类型”的函数的返回值绝对是不合法的。即使合法,由于c++“通过值(而不是引用)来返回对象”机制的原因,
    s.data[0]的一个拷贝会被修改,而不是s.data[0]本身

    3)一个成员函数为const的确切含义是什么呢?
    两种主要看法:
    ①数据意义上的const(bitwise constness)
    ②概念意义上的const(conceptual constness),又称logical constness
    bitwise constness:
    当且仅当成员函数不修改对象的任何数据成员(静态数据成员除外)时,即不修改对象中任何一个比特(bit)时,这个成员函数才是const的。
    实际上,bitwise constness正是c++对const问题的定义,const成员函数不被允许修改它所在对象的任何一个数据成员。
    但事情总没有绝对之分,就如同光既是直线传播的,也可以理解为光子一样。特例总会有的:
    很多不遵守bitwise constness定义的成员函数也可以通过bitwise测试。特别是,一个“修改了指针所指向的数据”的成员函数,其行为显
    然违反了bitwise constness定义,但如果对象中仅包含这个指针,这个函数也是bitwise const的,编译时会通过。(示例)

    class string {
    
    public:
    // 构造函数,使data指向一个value所指向的数据的拷贝
    string(const char *value);
    ...
    operator char *() const { return data;}
    private:
    char *data;
    };

    const string s = "hello"; // 声明常量对象
    char *nasty = s; // 调用 operator char*() const,取得指针
    *nasty = 'm'; // 修改s.data[0]
    cout << s; // 输出"mello"
    这就导致了logical constness引入:一个const成员函数可以修改它所在对象的一些数据(bits) ,但只有在客户端不会发觉的情况下。例:

    class string {
    public:
    // 构造函数,使data指向一个 value所指向的数据的拷贝
    string(const char *value): lengthisvalid(false) { ... }
    size_t length() const;
    private:
    char *data;
    size_t datalength; // 最后计算出的string的长度
    bool lengthisvalid; // 长度当前是否合法
    };
    
    size_t string::length() const {
    if (!lengthisvalid) {
    datalength = strlen(data); // 错误!
    lengthisvalid = true; // 错误!
    }
    return datalength;
    }

    这里length方法的实现显然不能让编译器接受,要想完成更改,可以使用关键字mutable:
    mutable size_t datalength;
    mutable bool lengthisvalid;
    这样即使在const成员函数中,这些成员变量也可以更改了。

    总结:关于const,其实没有一个太固定的概念,可以理解为bitwise constness我所欲也,logical constness亦我所欲也,二者不可兼得..

    4)如何更改const成员?
    引言:为什么还需要更改const成员呢?
    如果一个较庞大的const成员被各种反复引用,那么代码的重复部分就非常之多,伴随的编译时间、维护、代码膨胀等问题随之而来。
    在上面bitwise constness与logical constness不可兼得的年代,虽然mutable提供了一种解决方案,但是也有时候是其解决不了(比如mutable
    为加入C++标准的年代等)。
    解决方案①:通过类型转换消除const(不推荐)
    将一个const对象传递到一个取非const参数的函数中,同时你又知道参数不会在函数内部被修改的情况时。
    const char *klingongreeting = "nuqneh"; // "nuqneh"即"hello"
    char * length = const_cast<char*>(klingongreeting);
    注:只有在被调用的函数(比如本例中的strlen)不会修改它的参数所指的数据时,才能保证它可以正常工作。

    解决方案②:通过局部变量指针实现
    类c的一个成员函数中,this指针有如下的声明:
    c * const this; // 非const成员函数中
    const c * const this; // const成员函数中
    这种情况下(即编译器不支持mutable的情况下),如果想使那个有问题的string::length版本对const和
    非const对象都合法,就只有把this的类型从const c * const改成c * const。不能直接这么做,但可以通过
    初始化一个局部变量指针,使之指向this所指的同一个对象来间接实现。然后,就可以通过这个局部指针来访问
    你想修改的成员:

    size_t string::length() const {
    // 定义一个不指向const对象的局部版本的this指针
    string * const localthis = const_cast<string * const>(this);
    
    if (!lengthisvalid) {
    localthis->datalength = strlen(data);
    localthis->lengthisvalid = true;
    }
    return datalength;
    }

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

    附录1:
    ※关于const,static与static const
    const就是只读的意思,只在声明中使用;
    static一般有2个作用,规定作用域和存储方式.对于局部变量,static规定其为静态存储方式,每次调用的初始值为上一次调用的值,调用结束后存储空间不释放;
    对于全局变量,如果以文件划分作用域的话,此变量只在当前文件可见;对于static函数也是在当前模块内函数可见.
    static const 就是上面两者的合集.既是只读的,又是只在当前模块中可见的

  • 相关阅读:
    ElasticSearch集群配置
    基于visual Studio2013解决C语言竞赛题之0806平均分
    基于visual Studio2013解决C语言竞赛题之0805成绩输出
    基于visual Studio2013解决C语言竞赛题之0804成绩筛选
    基于visual Studio2013解决C语言竞赛题之0803报数
    基于visual Studio2013解决C语言竞赛题之0802图书信息查询
    基于visual Studio2013解决C语言竞赛题之0801信息输出
    基于visual Studio2013解决C语言竞赛题之0710排序函数
    基于visual Studio2013解决C语言竞赛题之0708字符串查找
    基于visual Studio2013解决C语言竞赛题之0707月份输出
  • 原文地址:https://www.cnblogs.com/hustcser/p/2731085.html
Copyright © 2011-2022 走看看