【C++学习笔记】何时需要const限定类成员函数?

本文通过实例探讨C++中何时需要在成员函数中使用const关键字。当函数不会修改对象状态,尤其是当函数接收const对象或参数为const时,应使用const限定。实验表明,const限定会影响函数返回值类型以及类指针的使用,确保不违反const语义。
#include <iostream>
class abc
{
    private:
        double x;
    public:
        abc(double y);
        abc();
        ~abc();
        abc& Comp(abc& n);
        void show();
};
abc::abc(double y)
{
    x=y;
}
abc::abc()
{
    x=0.0;
}
abc::~abc()
{
    
}
abc& abc::Comp(abc& n)
{
    if (n.x>x)
        return n;
    else 
        return *this;
}
void abc::show()
{
    std::cout<<"x="<<x<<std::endl;
}
int main() 
{
    abc A=abc(55.43);
    abc B=abc();
    abc *p=&B.Comp(A);
    p->show();
    return 0;
} 

//举个小栗子,代码中有一个类abc,一个私有成员x,一个对比两个对象的函数Comp和一个显式函数show

//上面的程序,不带任何const限定,程序可以通过编译并正常运行
//测试工具:DEV C++

//通过试验来了解何时使用const来限定成员函数,以及哪些限定位置是有联系的!

//我们知道,如果创建一个const类型的对象,那么调用它的成员函数需要进行const限定,因为无法保证该成员函数的代码是否会修改对象的数据,这就是需要使用const的时机!

//第一次实验,我们将Comp()函数的参数,改成const限定的参数,即“保证传递的引用对象不会被无意间修改

#include <iostream>
class abc
{
	private:
		double x;
	public:
		abc(double y);
		abc();
		~abc();
		abc& Comp(const abc& n);  //此处参数添加cosnt限定
		void show();
};
abc::abc(double y)
{
	x=y;
}
abc::abc()
{
	x=0.0;
}
abc::~abc()
{
	
}
abc& abc::Comp(const abc& n)
{
	if (n.x>x)
	    return n;
	else 
	    return *this;
}
void abc::show()
{
	std::cout<<"x="<<x<<std::endl;
}
int main() 
{
	abc A=abc(55.43);
	abc B=abc();
	abc *p=&B.Comp(A);
	p->show();
	return 0;
}
编译错误提示:[Error] invalid initialization of reference of type 'abc&' from expression of type 'const abc'
错误原因是,Comp函数传递的是一个const对象引用,而函数把这个const引用作为返回值,但Comp本身的返回值类型,也就是原型中并没有声明返回值是一个const对象。因此,编译器会报错。假如把Comp函数中的返回值n换成*this,也就是说不把传递进来的cosnt对象引用n作为返回值,程序会正常编译,冲突消失;
正确的解决方案是:Comp函数参数带有const限定,并被返回,那么无论是原型还是定义中,参数和返回值都需要const限定;

结论:如果函数参数是const限定的,并被返回,函数的返回值也需要const限定

第二次实验,按照第一个实验结论修改程序,我们重新运行,但仍然报错,这里报错的是下列两个语句:

/*
abc *p=&B.Comp(A);
p->show();
*/
#include <iostream>
class abc
{
	private:
		double x;
	public:
		abc(double y);
		abc();
		~abc();
		const abc& Comp(const abc& n);
		void show();
};
abc::abc(double y)
{
	x=y;
}

abc::abc()
{
	x=0.0;
}
abc::~abc()
{
	
}
const abc& abc::Comp(const abc& n)
{
	if (n.x>x)
	    return n;
	else 
	    return *this;
}
void abc::show()
{
	std::cout<<"x="<<x<<std::endl;
}
int main() 
{
	abc A=abc(55.43);
	abc B=abc();
    //abc *p=&B.Comp(A);
	//p->show();

	return 0;
}
我们先不管这两条语句为什么出错,先让注释掉,再运行,程序正常编译;
编译上述代码无误;
       我们先添加一条这样的代码:B.Comp(A);,再次运行,仍然会正确编译;注意Comp函数返回的是一个cosnt限定的对象引用
假设,我们根据额外添加的这条语句,想直接调用show函数来查看x的值,因为函数返回的是对象引用,因此可以直接用上条语句调用show函数,即B.Comp(A).show();不幸的是,编译报错:[Error] passing 'const abc' as 'this' argument of 'void abc::show()' discards qualifiers [-fpermissive];错误原因就是文章开头所说的,B.Comp(A)这个函数返回的是一个const类型的对象,它不同于A和B,而一个独立的abc类的,且被const限定了,这个时候调用show函数,由于show函数没有被const限定,它的代码可能会导致无意间修改了这个const对象的值,这就是错误的根源;所以正确的做法是,在show()的后面添加const限定;
为什么在后面限定?第一,show没有返回值,不能放到前面,因为前面是限定返回值类的;第二,show没有参数,因此没法在括号中去限定参数,因此只能放到后面来限定show函数;因此,show函数成为了“const限定的类成员函数”;
error:B.Comp(A).show();
修改原型:void show() const;
修改定义:void abc::show() const;
修改后,程序可以正常编译和运行
结论:调用成员函数的对象如果是const限定的,这个被调用的函数也需要const限定

第三次实验,将上述代码中暂时注释的两条语句去掉注释,进行编译报错;

这个错误非常明显,由于定义的abc类指针没有被const限定,而后面的函数返回的是一个const类型的对象,造成冲突(注意,返回对象需要使用取地址运算符将地址赋值给指针)

修改:在类指针前添加const限定,cosnt abc* p = &B.Comp(A);

//下面是正确的代码:
#include <iostream>
class abc
{
    private:
        double x;
    public:
        abc(double y);
        abc();
        ~abc();
        const abc& Comp(const abc& n);
        void show() const;
};
abc::abc(double y)
{
    x=y;
}

abc::abc()
{
    x=0.0;
}
abc::~abc()
{
    
}
const abc& abc::Comp(const abc& n)
{
    if (n.x>x)
        return n;
    else
        return *this;
}
void abc::show() const
{
    std::cout<<"x="<<x<<std::endl;
}
int main()
{
    abc A=abc(55.43);
    abc B=abc();
    B.Comp(A).show();
    const abc *p=&B.Comp(A);
    p->show();
    return 0;
}

//第四次实验,我们发现,没有在show函数后面添加const限定的时候,仅使用语句B.Comp(A)时,程序不会报错,仅在用它来调用show函数时出错;通过前面的了解,我们知道Comp函数的返回值是一个const限定的对象引用,而必须这样的原因是,函数代码块中存在一个返回const对象的语句,而这个根源就是参数是const型的,因此,一旦满足参数是const限定的,返回值是const限定的,就必须在该加const的位置上添加const;

那Comp函数需不需要const限定?

这取决于你会不会定义一个const限定的类对象,然后用这个类对象去调用Comp了。因此,在主函数中添加下列代码将报错:
/*
    const abc C=abc(34.6); //C是一个const对象
    C.Comp(B); //ERROR:而Comp不是const限定的,即便Comp函数的代码并没有改变任何对象的值;
*/
正确的做法是,在Comp函数的原型和定义后面添加const限定;
总结:何时使用const?
1.在你不打算修改,并且也为了防止无意间修改变量(引用,指针,对象等)的位置添加const限定;
2.非const允许匹配const以及非const,但const必须匹配const;比如返回值类型中返回值是const,返回值类型必须是const;但返回值是const,将一个非const返回给const没问题

//最终的代码
#include <iostream>
class abc
{
    private:
        double x;
    public:
        abc(double y);
        abc();
        ~abc();
        const abc& Comp(const abc& n) const;
        void show() const;
};
abc::abc(double y)
{
    x=y;
}

abc::abc()
{
    x=0.0;
}
abc::~abc()
{
    
}
const abc& abc::Comp(const abc& n) const
{
    if (n.x>x)
        return n;
    else
        return *this;
}
void abc::show() const
{
    std::cout<<"x="<<x<<std::endl;
}
int main()
{
    abc A=abc(55.43);
    abc B=abc();
    B.Comp(A).show();
    const abc *p=&B.Comp(A);
    p->show();
    const abc C=abc(34.6);
    C.Comp(B);
    return 0;
}


//PS:代码仅供参考,试验中的语句为了考察const的限定和用法,可能存在不好的地方!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值