• const 成员函数与基于 const 的重载


    const 成员函数

    const 成员函数的作用是允许 const 对象使用成员函数。

    以下内容来自《C++ Primer 5th》:

    默认情况下 this 指针的类型是指向类类型非常量版本的常量指针。具体来说就是:StrBlob* const(StrBlob 是一个类),这也就意味着,this 指针无法指向一个常量 StrBlob 对象,也就无法在常量 StrBlob 对象上使用普通的成员函数。

    所以我们想让 this 变为指向常量的常量指针,C++ 中的做法是在普通成员函数的参数列表后面加上 const 关键字。这样的成员函数叫做常量成员函数

    class StrBlob{
        friend ostream& operator << (ostream&, const StrBlob&);
        friend istream& operator >> (istream&, StrBlob&);
    public:
        typedef vector<string>::size_type size_type;
        StrBlob() :data() {}
        StrBlob(initializer_list<string> il) :data(make_shared<vector<string>>(il)) {}
        void push_back(const string& s) { data->push_back(s); }
        size_type size() const { return data->size(); }
        bool empty() const { return data->empty(); }
        void pop_back() const;
    private:
        shared_ptr<vector<string>> data;
        void check(size_type i, const string& msg) const;
    };
    

    基于 const 的重载

    简单来说,基于 const 重载背后的思想大致是:让非常量对象使用非常量成员函数,让常量对象使用常量成员函数。

    这个重载根据调用对象是否为常量,调用相应的成员函数。

    下面的示例代码来自《C++ Primer 5th》:

    class Screen{
    public:
        Screen &display(std::ostream& os) { do_display(os); return *this; }
        const Screen &display(std::ostream& os) const {
            do_display(os);
            return *this;
        }
    private:
        void do_display(std::ostream &os) const { os << contents; }
    };
    

    如果是一个常量对象,display 会调用 const Screen &display(std::ostream& os) const
    如果是一个非常量对象,display 会调用 Screen &display(std::ostream& os)

    一些想法

    上面的内容在书中都可以找到,确实没什么意思。

    在最初接触时,我有点觉得基于 const 的重载是一个有些没什么用的特性,因为无论是常量还是非常量对象都可以调用 const 成员函数,那基于 const 的重载只不过是更严格了一些,好像用处不是很大。但其实完全不是这样。

    不妨看一个例子:

    // StrBlob.h
    #pragma once
    #include <iostream>
    #include <memory>
    #include <vector>
    #include <stdexcept>
    
    using namespace std;
    
    class StrBlob{
        friend ostream& operator << (ostream&, const StrBlob&);
        friend istream& operator >> (istream&, StrBlob&);
    public:
        typedef vector<string>::size_type size_type;
        StrBlob() :data() {}
        StrBlob(initializer_list<string> il) :data(make_shared<vector<string>>(il)) {}
        void push_back(const string& s) { data->push_back(s); }
        size_type size() const { return data->size(); }
        bool empty() const { return data->empty(); }
        void pop_back() const;
        string& front() const;
        string& back() const;
    private:
        shared_ptr<vector<string>> data;
        void check(size_type i, const string& msg) const;
    };
    

    这里略去了具体成员函数的部分实现。详见《C++ Primer 5th》第十二章。

    #include "StrBlob.h"
    
    int main()
    {
        StrBlob b1;
        StrBlob b2 = {"a", "an", "the"};
        b1 = b2;
        b2.push_back("some");
        const StrBlob bc = {"1", "2", "3"};
        bc.front() = "4444";
        cout << bc.front() << endl;
        return 0;
    }
    

    你会发现这部分代码完全可以通过编译,而且会出现一个灾难性的问题:const 对象 bc 中的值被改变了。

    这完全合法(至少我直觉上认为),因为 bc 中的 data 属性地址没变,变化的只是 data 中的一个元素。

    但是这可能并不是我们想要的,我觉得我们定义了一个 const 对象,就是希望其中的数据不会被改变,就像我们定义 const int a = 42; 一样。

    那问题出在哪呢?就出在这个 const 成员函数上,front() 显然返回一个字符串引用,修改一个字符串引用绑定的对象总不是错的吧?这就导致我们虽然好像定义了 const 对象,但是被我们自己的成员函数实现给否决了。

    所以这时候就看出基于 const 的重载的作用了,他会根据 this 指针是否指向常量对象选择重载的成员函数,一个正确的类定义应该是下面这样:

    #pragma once
    #include <iostream>
    #include <memory>
    #include <vector>
    #include <stdexcept>
    
    using namespace std;
    
    class StrBlob{
        friend ostream& operator << (ostream&, const StrBlob&);
        friend istream& operator >> (istream&, StrBlob&);
    public:
        typedef vector<string>::size_type size_type;
        StrBlob() :data() {}
        StrBlob(initializer_list<string> il) :data(make_shared<vector<string>>(il)) {}
        void push_back(const string& s) { data->push_back(s); }
        size_type size() const { return data->size(); }
        bool empty() const { return data->empty(); }
        void pop_back() const;
        string& front();
        const string& front() const;
        string& back();
        const string& back() const;
    private:
        shared_ptr<vector<string>> data;
        void check(size_type i, const string& msg) const;
    };
    

    这个时候再执行上面的主函数,因为返回的是一个指向常量的字符串引用,所以并不可以对其进行赋值修改。

    当然还是要具体情况具体分析,我个人感觉那些返回对象数据引用或指针的就需要使用基于 const 重载的 const 成员函数。防止对 const 对象数据进行误修改。

  • 相关阅读:
    Unity3D Asset文件导出3DMax 可编辑格式
    Android 内存管理
    常见面试之机器学习算法思想简单梳理
    最短的计算大数乘法的c程序
    MQTT---HiveMQ源代码具体解释(一)概览
    ZMQ源代码分析(一)-- 基础数据结构的实现
    JavaScript 性能分析新工具 OneProfile
    firefox关于about:config的常用配置
    postgres数据库中的数据转换
    postgres的强制类型转换与时间函数
  • 原文地址:https://www.cnblogs.com/LuoboLiam/p/13810797.html
Copyright © 2020-2023  润新知