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方法。

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

原文地址:https://www.cnblogs.com/rolling-stone/p/3223454.html