effective c++ 学习心得(二)

条款03(尽量使用const)

use const whenever possible

引用原文关于const的描述

它允许你指定一个语义约束(也就是制定一个不该被改动的对象,而编译器会实施这项约束。

const的使用范围

你可以用它在classes外部修饰global或者namespace作用域的常量或者修饰文件,函数,或
区块作用域中被声明为static对象。你也可以用它修饰classes内部的static和non-static成员变
量。面对指针,你也可以指出指针自身,指针所指物,或两者都(或都不)是const

例如:
    char greet[] = "Hello";
    char *p1= greet; // non-const pointer, non-const data 指针和指针指向地址的内容都可以改变
    const char *p2 = greet; // non-const pointer, const data 指针的内容可以改变,指针指向地址的内容不能改变
    char* const p3 = greet; // const pointer, non-const data  指针的内容不能改变,指针指向地址的内容改变
    const char* const p4 = greet; // const pointer, const data  指针的内容不能改变,指针指向地址的内容不能改变
    
    //上面几个例子展现了const用于指针时的几种不同情况,在实际情况下根据自己需要使用。
    

const在函数中最具威力

const可以和函数参数,函数返回值,函数自身(成员函数)产生关联。
举个例子:

#include <iostream>

class Wid{
public:
        void get(int &a){
                if(a = 1){   // 将 == 误写成 =
                        std::cout << "in get() the a is " << a << std::endl;
                }
        }
};

int main(){
        Wid w;
        int a = 2;  // a最开始的值是2
        w.get(a);
        std::cout << "the a is " << a << std::endl;
        return 0;
}

运行结果:
    [m@m ~/practice]$ ./7.out
    in get() the a is 1
    the a is 1
    // 从以上结果我们可以知道如果我们在写代码时,如果将==误写成=,那么可能就会将一些我们
    // 不想改变的值给改变,a就是这样的。

所以有时候const是我们程序代码的帮手,可以帮我们检查程序中本不该发生的错误。

// 给int &a 前面加上const,其他不变,不出意外我们可以看到程序报错的消息
#include <iostream>

class Wid{
public:
        void get(const int &a){
                if(a = 1){
                        std::cout << "in get() the a is " << a << std::endl;
                }
        }
};

int main(){
        Wid w;
        int a = 2;
        w.get(a);
        std::cout << "in main a is " << a << std::endl;

        return 0;
}
运行结果:
    [m@m ~/practice]$ !clang
    clang++ 7.cpp -o 7.out
    7.cpp:6:10: error: cannot assign to variable 'a' with const-qualified type 'const int &'
                            if(a = 1){
                               ~ ^
    7.cpp:5:23: note: variable 'a' declared const here
            void get(const int &a){
        1 error generated.


#### const成员函数
const作用于成员函数中时,是不可改变成员变量的,但是全局变量,全局静态变量,包括还有类中的类静态成员变量都是可以
被常成员函数修改的。

为什么说const成员函数比较重要呢?有两个原因:
1.它可以让类知道那些函数是可以修改对象内容(成员变量的,那些是不可以的。
2.它们是操纵const对象成为可能。

    // const作用于成员函数中时,是不可改变成员变量的
    #include <iostream>

    class Wid{
    public:
            Wid(int i){
                    a = i;
            }
            void get() const{
                    a++:  // a是成员变量,是不可以修改的
            }
    private:
            int a;
    };

    int main(){

            Wid w(1);
            w.get();
            return 0;
    }

    运行结果:
        [m@m ~/practice]$ !clang
        clang++ 7.cpp -o 7.out
        7.cpp:9:6: error: cannot assign to non-static data member within const member function 'get'
       a++:
       ~^
        7.cpp:8:8: note: member function 'Wid::get' is declared const here
                void get() const{
                ~~~~~^~~~~~~~~~~
        1 error generated.
        

关于第二点,如果两个成员函数只是常量性不同,可以被重载,可以使用const修饰其中一个函数,用来区分重载

    #include <iostream>
    #include <string.h>


    class Wid{
    public:
            Wid(std::string s){
                    text = s;
            }

            char& operator[](std::size_t position){
                    std::cout << "ordinary object" << std::endl;
                    return text[position];
            }

            const char& operator[](std::size_t position) const {
                    std::cout << "const object" << std::endl;
                    return text[position];
            }

    private:
            std::string text;

    };

    int main(){
            Wid w("hello"); // 一般对象
            const Wid w1("world");  // 常对象
            // w[1] = 'x';  //可以修改
            // w1[2] = 'x';  // 不可以修改,由于w1是常对象,调用常函数,返回的是const char &,所以修改它是错误的。
            w[1];
            w1[1];
            return 0;

    }
    运行结果:
    ordinary object
    const object
    
    可以看到两个重载操作符调用了不同的函数。
    
    
随着继续学习我们可以思考一下是否常函数真的不能修改成员变量?答案是否定的。
**利用c++中一个与const相关的摆动场:mutable(可变的),可以实现在常函数中修改成员变量。**

    #include <iostream>


    class Wid{
    public:
            Wid(int i){
                    a = i;
            }
            void change() const {
                    a++;
                    std::cout << "the a is " << a << std::endl;
            }
    private:
            mutable int a;
    };

    int main(int argc, char *argv[argc])
    {
        Wid w(1);
            w.change();
        return 0;
    }
    
    运行结构:
    the a is 2  // 可以看到a确确实实被修改了,但没什么必要就不要改变常函数的行为了,容易使代码出现混乱。
    
#### 在const和non-const成员函数中避免重复
首先贴一段上文的代码片段:

        char& operator[](std::size_t position){
                ... // 一些处理代码
                return text[position];
        }
        const char& operator[](std::size_t position) const {
                ... //同上,相同的处理代码
                return text[position];
        }
        
我们可以看到除了函数名不同,函数体的代码都相同(为了演示,实际不一定相同),造成了代码重复,
为了避免这种情况,我们可以使用另外一种方法,来实现在一个函数体中调用另一个函数中的代码

    #include <iostream>
    #include <string.h>


    class Wid{
    public:
            Wid(std::string s){
                    text = s;
            }

            char& operator[](std::size_t position){
                    // 代码有两次转型,第一次:将*this 从 static_cast<const Wid&> 是为了将Wid& 转换为const Wid&
                    // 第二次: 将返回值类型从const char & 转换为char &
                    return const_cast<char&>(static_cast<const Wid&>(*this)[position]); 
            }

            const char& operator[](std::size_t position) const {
                    return text[position];
            }

    private:
            std::string text;

    };

    int main(){
            const Wid w1("world");
            std::cout << "the w1 is " << w1[1] << std::endl;
            return 0;

    }
    
    运行结果:
    the w1 is o


***我们在non-const函数中调用了const函数来避免了代码的重复,那是否可以反向调用,使其在const函数中调用non-const函数呢?
最好不要,因为const函数承诺绝不改变函数的逻辑状态,而我们在const函数中调用non-const函数违背了这一准则,因为曾经承
诺的那个对象被修改了***

**总结:**
1.将某些东西声明为const,可帮助编译器检测出错误。const可作用于任何作用域对象,函数参数,函数返回类型,成员函数本体。
2.const和non-const成员函数有着实质的相同实现时,令non-const 调用 const 的版本可避免代码重复。


有哪些不对的地方,欢迎指正。

Micon
12 声望7 粉丝

enjoy simplicity