zoukankan      html  css  js  c++  java
  • C++中const——由一个例子想到的

    前天同学实现了《C++ Primer》中关于虚函数的一个例子,拿过来问我,代码如下:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Item{
    public:
        Item(const string x,const double y){isbn=x;price=y;};
        virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别
        virtual ~Item(){};
        string isbn;
    protected:
        double price;
    };
    
    class Bulk_Item:public Item{
    public:
        Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};
        double net_price(size_t n) const;
        //double get() const;
        //double get();
    private:
        size_t min_qty;
        double discount;
    };
    
    double Bulk_Item::net_price(size_t n) const{
        if(n>=min_qty) return n*(1-discount)*price;
        else return  n*price;
    }
    
    void print_total(const Item &it,size_t n){
        cout<<it.isbn<<"	number sold: "<<n<<"	total price: "<<it.net_price(n)<<endl;
    }
    
    int main(){
        Item it1("201307291540",16.5);
        cout<<it1.net_price(2)<<endl;
        Bulk_Item it2("201307291545",22.5,10,0.2);
        cout<<it2.net_price(20)<<endl;
        print_total(it1,4);
        print_total(it2,20);
        return 0;
    }

     同学的疑问是,如果去掉 Item 类定义中

     virtual double net_price(size_t n) const{return n*price;};

    一句中的 const 限定,则会在print_total 的定义行出现编译错误

    “Item::net_price”: 不能将“this”指针从“const Item”转换为“Item &”
    

    他觉得定义的 net_price 方法并没有修改 Item 内的属性,有没有const限定词应该是一样的。 

    但实际上,在print_total 的定义中,限定了引用 it 为 const, 这意味着该引用只能调用 Item 类内的 const方法,即:const限定的对象引用不能调用此对象中的非const方法。

    进而,可以知道,C++中判断一个方法是不是const,并不是检测你在该方法中有没有改变类的属性,而只是简单的看你有没有const限定词,有就是const方法,没有就不是const方法。若有const限定词,而在方法内你又试图改变类的属性,则编译器会报错。

    那么,同名函数(包括参数相同),一个有const限定,一个没有,是两个函数还是一个函数?为了探究,在 Bulk_Item 的定义中增加两个 get_price 函数,一个为const,一个为普通函数,然后在主函数里调用:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Item{
    public:
        Item(const string x,const double y){isbn=x;price=y;};
        virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别
        virtual ~Item(){};
        string isbn;
    protected:
        double price;
    };
    
    class Bulk_Item:public Item{
    public:
        Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};
        double net_price(size_t n) const;
    
        double get_price() const{
            cout<<"get const"<<endl;
            return price;
        };
        double get_price(){
            cout<<"get Non const"<<endl;
            return price;    
        };
    
    
    private:
        size_t min_qty;
        double discount;
    };
    
    double Bulk_Item::net_price(size_t n) const{
        if(n>=min_qty) return n*(1-discount)*price;
        else return  n*price;
    }
    
    void print_total(const Item &it,size_t n){
        cout<<it.isbn<<"	number sold: "<<n<<"	total price: "<<it.net_price(n)<<endl;
    }
    
    int main(){
        Item it1("201307291540",16.5);
        cout<<it1.net_price(2)<<endl;
        Bulk_Item it2("201307291545",22.5,10,0.2);
        cout<<it2.net_price(20)<<endl;
        print_total(it1,4);
        print_total(it2,20);
    
        cout<<it2.get_price()<<endl;
       
        return 0;
    }

    输出结果为:

    get Non const
    22.5

    可见,并没有产生编译错误,自动调用的是非const方法。故有无const会造成有两个重载的函数,编译后的函数名,除了函数名和参数,还加入了const限定词。但调用时会优先选非const的函数(这是因为我们传入的是非const对象it2,下面会看到,若传入const对象,则自动调用const版本)。

    接着,我们构造打印函数,如下:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Item{
    public:
        Item(const string x,const double y){isbn=x;price=y;};
        virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别
        virtual ~Item(){};
        string isbn;
    protected:
        double price;
    };
    
    class Bulk_Item:public Item{
    public:
        Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};
        double net_price(size_t n) const;
    
        double get_price() const{
            cout<<"get const"<<endl;
            return price;
        };
        double get_price(){
            cout<<"get Non const"<<endl;
            return price;    
        };
    
    
    private:
        size_t min_qty;
        double discount;
    };
    
    double Bulk_Item::net_price(size_t n) const{
        if(n>=min_qty) return n*(1-discount)*price;
        else return  n*price;
    }
    
    void print_total(const Item &it,size_t n){
        cout<<it.isbn<<"	number sold: "<<n<<"	total price: "<<it.net_price(n)<<endl;
    }
    
    void print_price(const Bulk_Item &it){
        cout<<"print_price const"<<endl;
        cout<<it.get_price()<<endl;
    }
    
    void print_price(Bulk_Item &it){
        cout<<"print_price Non const"<<endl;
        cout<<it.get_price()<<endl;
    }
    
    int main(){
        Item it1("201307291540",16.5);
        cout<<it1.net_price(2)<<endl;
        Bulk_Item it2("201307291545",22.5,10,0.2);
        cout<<it2.net_price(20)<<endl;
        print_total(it1,4);
        print_total(it2,20);
    
        cout<<it2.get_price()<<endl;
        print_price(it2);
    
    
        return 0;
    }

    输出结果为:

    print_price Non const
    get Non const
    22.5

    可见,(1)调用时,优先调用参数为非const的函数(因为传入的it2是非const参数),编译器不会在你传入非const参数时调用const参数的函数(除非没有非const版本),这是合理的,否则你不能改变你想改变的;(2)函数内,自动调用非const版本get_price(因为传入的是非const引用,故优先调用get_price方法的非const版本)。

    同理,如果传入const 参数,如下:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Item{
    public:
        Item(const string x,const double y){isbn=x;price=y;};
        virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别
        virtual ~Item(){};
        string isbn;
    protected:
        double price;
    };
    
    class Bulk_Item:public Item{
    public:
        Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};
        double net_price(size_t n) const;
    
        double get_price() const{
            cout<<"get const"<<endl;
            return price;
        };
        double get_price(){
            cout<<"get Non const"<<endl;
            return price;    
        };
    
    
    private:
        size_t min_qty;
        double discount;
    };
    
    double Bulk_Item::net_price(size_t n) const{
        if(n>=min_qty) return n*(1-discount)*price;
        else return  n*price;
    }
    
    void print_total(const Item &it,size_t n){
        cout<<it.isbn<<"	number sold: "<<n<<"	total price: "<<it.net_price(n)<<endl;
    }
    
    void print_price(const Bulk_Item &it){
        cout<<"print_price const"<<endl;
        cout<<it.get_price()<<endl;
    }
    
    void print_price(Bulk_Item &it){
        cout<<"print_price Non const"<<endl;
        cout<<it.get_price()<<endl;
    }
    
    int main(){
        Item it1("201307291540",16.5);
        cout<<it1.net_price(2)<<endl;
        Bulk_Item it2("201307291545",22.5,10,0.2);
        cout<<it2.net_price(20)<<endl;
        print_total(it1,4);
        print_total(it2,20);
    
        cout<<it2.get_price()<<endl;
        print_price(it2);
    
        const Bulk_Item it3("201307291546",44,10,0.2);
        print_price(it3);
    
        return 0;
    }

    则会打印:

    print_price const
    get const
    44

    说明,(1)传入const参数会调用参数为const的函数,这是理所应当的;(2)在print_price里会调用const版本的get_price,这说明,如果我们对一个类,有同名的两个函数,一个为const,一个非const,若用一个const对象引用来调用这个同名函数,则自动调用那个const函数。如下所示:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Item{
    public:
        Item(const string x,const double y){isbn=x;price=y;};
        virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别
        virtual ~Item(){};
        string isbn;
    protected:
        double price;
    };
    
    class Bulk_Item:public Item{
    public:
        Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};
        double net_price(size_t n) const;
    
        double get_price() const{
            cout<<"get const"<<endl;
            return price;
        };
        double get_price(){
            cout<<"get Non const"<<endl;
            return price;    
        };
    
    
    private:
        size_t min_qty;
        double discount;
    };
    
    double Bulk_Item::net_price(size_t n) const{
        if(n>=min_qty) return n*(1-discount)*price;
        else return  n*price;
    }
    
    void print_total(const Item &it,size_t n){
        cout<<it.isbn<<"	number sold: "<<n<<"	total price: "<<it.net_price(n)<<endl;
    }
    
    void print_price(const Bulk_Item &it){
        cout<<"print_price const"<<endl;
        cout<<it.get_price()<<endl;
    }
    
    void print_price(Bulk_Item &it){
        cout<<"print_price Non const"<<endl;
        cout<<it.get_price()<<endl;
    }
    
    int main(){
        Item it1("201307291540",16.5);
        cout<<it1.net_price(2)<<endl;
        Bulk_Item it2("201307291545",22.5,10,0.2);
        cout<<it2.net_price(20)<<endl;
        print_total(it1,4);
        print_total(it2,20);
    
        cout<<it2.get_price()<<endl;
        print_price(it2);
    
        const Bulk_Item it3("201307291546",44,10,0.2);
        cout<<it3.get_price()<<endl;
        print_price(it3);
    
        return 0;
    }

     则会打印:

    get const
    44

     故而,鉴于有无const限定词会造就两个不同的函数,所以如果基类中有const,而继承类中同名方法没有const,则其实继承类实现的是一个完全新的函数,而不是在覆盖基类的方法。向上类型转换时,会调用基类的方法,而不是继承类中同名的非const方法,如下:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Item{
    public:
        Item(const string x,const double y){isbn=x;price=y;};
        virtual double net_price(size_t n) const{return n*price;};//可以去掉virtual体验到第3、4行打印出来的区别
        virtual ~Item(){};
        string isbn;
    protected:
        double price;
    };
    
    class Bulk_Item:public Item{
    public:
        Bulk_Item(const string x,const double y,const size_t z,const double w):Item(x,y),min_qty(z),discount(w){};
        double net_price(size_t n) ;
    
    private:
        size_t min_qty;
        double discount;
    };
    
    double Bulk_Item::net_price(size_t n) {
        if(n>=min_qty) return n*(1-discount)*price;
        else return  n*price;
    }
    
    void print_total(const Item &it,size_t n){
        cout<<it.isbn<<"	number sold: "<<n<<"	total price: "<<it.net_price(n)<<endl;
    }
    
    
    
    int main(){
        Item it1("201307291540",16.5);
        cout<<it1.net_price(2)<<endl;
        Bulk_Item it2("201307291545",22.5,10,0.2);
        cout<<it2.net_price(20)<<endl;
        print_total(it1,4);
        print_total(it2,20);
    
        
        return 0;
    }

    最后一行输出为450而不是360。

    同理,若基类方法无const,而继承类中有,则向上类型转换还是会调用基类的方法。

    总结一下,主要有这么几点:

    1、const限定的对象引用不能调用此对象中的非const方法。

    2、const关键词限定的方法,则编译器就认为它是const型的,即使它并没有改变对象的任何属性。若限定了const,又在方法内改变了属性,则编译器会报错。

    3、同名函数(参数也相同),一个有const限定,一个没有,编译器认为是两个重载函数,不会报错。这就意味着C++中编译器生成函数名时,除了函数名、参数外还有const,由三者共同组成编译后的函数名。

    4、如上,两个方法的方法名和参数完全相同,一个有const限定,一个没有,则若你用const对象引用调用,会自动调用const版本的方法;若用非const对象引用调用,则会自动调用非const的版本。

    5、同样的函数,只是参数一个是const,一个非const,则你用const参数调用,会自动调用参数为const的版本;你用普通参数调用,会自动调用参数为普通参数的版本,若此时没有非const的版本,它才会转而调用const版本的。

    6、如果基类中有const,而继承类中同名方法没有const,则其实继承类实现的是一个完全新的函数,而不是在覆盖基类的方法,向上类型转换时,会调用基类的方法,而不是继承类中同名的非const方法。

    突然想到,学习一门语言的最好方式也就就是:写一个这个语言的编译器。这样你就能对它的限制,它能做什么和不能做什么,你能用它做什么和不能做什么,有比较深刻的理解和记忆。推而广之,人类语言有很多冗余性,语法限制也相对比较宽松,故人类语言的编译器应该很难写。若能写一个人类语言的编译器,那就是真正的自然语言理解。

  • 相关阅读:
    mongodb数组操作
    tmux使用心得
    redis设置key总结
    gitbook构建文档命令
    js中for in,of区别
    redis清除缓存和连接远程服务器
    Postman使用记录
    asp.net中导出Execl的方法
    CASE WHEN 用法
    js 字符串转换数字
  • 原文地址:https://www.cnblogs.com/rolling-stone/p/3223454.html
Copyright © 2011-2022 走看看