跟我学C++中级篇——常函数

发布于:2025-08-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、函数

函数在前面已经讲过很多次,不同的角度不同的层次,对函数都进行了说明。很多开发者,特别是初学者可能不太明白,为什么要对函数做各种限制,又在限制的同时,偶尔会放出一个一个的小口子,让初学者感到非常迷惑。甚至有的人说函数就函数呗,为什么搞这么多的条件限制。
其实,这些开发者经历多了以后,就会渐渐明白,这些限制不是说拍脑袋制定出来的,它是实际需求产生的。举一个现实的例子就明白了,汽车上为什么会有转向灯,为什么后来又有日行灯?普通人骑自行车也没有这些东西啊,不照样好好的。其实,都是为了安全,转向灯就不用说了,说一下日行灯,这个功能本来是高纬度地区(冬季白昼时间短),为了防止人们(特别是路人和其它车辆)不知道车辆运行中的一个安全提示,慢慢演变成了现在几乎所有车有拥有日行灯的一个安全标准。
同样,开发中往往也遇到一些类似的问题,需要对函数的行为进行约束或对其它代码管理者的给予一种提示。

二、常函数

今天重点从只读限制角度对函数进行分析一下,一般来说,限制类成员函数只能读取类成员的函数,叫做常成员函数也就是常函数。试想一下,开发者会不会遇到这种场景,设计的类中提供了一个接口,但这个接口提供给一个分析相关的客户,正常情况下,它们只会对通过这个接口对数据进行读取。而所有的开发者都清楚,人会犯各种错误的,比如好奇心、恶意毁坏数据等等。如果有人不小心通过这个接口写入了数据,而且后来的开发者又在某些情况使用了这些数据,结果会是怎样?
所以需要从形式就避免这种情况,常函数则恰恰解决了这种场景。不管接口是否写入数据,只要在常函数里对成员变量进行了修改,或者调用了非常函数,那么,编译器就会报错,这就是常函数的显式的安全控制作用。这和前面提到的日行灯是不是有异曲同工之妙?
常函数的定义形式如下:

return_type  function_name (parameter_list) const  {body}

有过开发经验的都知道,每个类成员的函数中都有一个隐藏的this指针,这也是开发者在成员函数内部调用其它成员时可以使用this指针操作的原因。this指针是一个指针常量,意味着this指针不能被重新赋值。而在常函数中,不但这个this指针是一个常量,其指定的内容也是常量即常量指针,所以调用this即不可以改变指针本身也不能修改指针指向的内容。
常函数的特点主要有:
1、常函数不能修改对象的成员变量
2、常函数内只能调用常函数
3、常函数如果必须修改类成员变量,则此成员变量必须使用mutable关键字修饰
4、常函数可以被任何类型的对象调用,但常量对象只能调用常函数
5、常函数和非常函数可以形成重载,它们会优先被同样的类型对象调用(常对象调用常函数但不能调用非常函数,非常对象调用非常函数可以调用常函数)
6、常函数可以提高编译器对相关代码的优化。常函数明确不会修改成员变量,编译器会据此进行更多的常量折叠等的优化,提高编译和运行的效率。
常函数的使用大提高了代码的安全性,降低了代码阅读和维护的复杂度,让调用者和开发维护者都可以更快速准确的定位相关的问题所在,杜绝了非合法接口的数据污染的可能性。在新的C++标准中,引入了constexpr函数和consteval函数,对const函数进行了更细致的处理。大家要及时跟进相关的标准的发展,确保在新标准的编译器下进行更安全精细的代码编写。

三、应用和例程

常函数的应用场景主要包括:
1、多线程中的安全使用,常函数不修改数据,避免同步的操作
2、优化特定的代码,包括编译优化和运行优化
3、对象数据只读(防止出现意外修改对象成员),包括常对象的级联调用即函数参数为常量时调用相关函数
4、提供常函数的重载(需要只读和读写两种情况时)
5、更清晰安全的提供接口设计

下面看一个常函数的定义和使用的例子:

#include<iostream>

class Demo
 {
public:
    int GetData() {
        return data_;
    }

    int GetData ()const {
        return data_;
    }

    void DisplayData() {
        std::cout << "cur data: " << data_ << std::endl;
    }
    void SetData(int d) const { 
        data_ = d;//err
    }
    void testConst() const{
    std::cout<<"test const function!"<<std::endl;
    }

private:
    int data_ = 0;
};
//error:A constant function can only be a constant member function
// void GlobalDemo() const{
    // std::cout<<"test global!"<<std::endl;
// }
int main()
{
    Demo d;
    d.DisplayData(); 
    d.SetData(3);//err
    
    const Demo* d2 = new Demo; 
    d2->testConst();
    
    const Demo d3; 
    d3.testConst();
    d3.DisplayData();//err:Constant objects can only call constant methods
    
    return 0;
}

四、总结

正如许多的开发细节,为初学者不好理解,甚至感觉到繁复。但实际的应用中,发现这些细节都是前面的开发者用教训换来的而不是故意为难后来者的一种手段。现实世界中也是如此,典型的就是交通规则中的一些内容,看上去没有什么意义(比如开车前绕车转一圈),但可能是用生命换回来的教训。


网站公告

今日签到

点亮在社区的每一天
去签到