c++ const 修饰函数

const是衡量一个程序员是否老道的一个标准,除了修饰变量之外,还可以修饰函数,主要有以下几种形式

const int& fun(int& a); //修饰返回值
int& fun(const int& a); //修饰形参
int& fun(int& a) const{} //const成员函数

const返回值

这种多是修饰返回值是引用类型的情况下,为了避免返回值被修改的情况。

解释下:返回值是引用的函数, 可以肯定的是这个引用必然不是临时对象的引用, 因此一定是成员变量或者是函数参数, 所以在返回的时候为了避免其成为左值被修改,就需要加上const关键字来修饰。

举个例子:

#include<iostream>

using namespace std;

class A
{
private:
    int data;
public:
    A(int num):data(num){}
    ~A(){};
    int& get_data()
    {
        return data;
    }
};

int main()
{
    A a(1);
    a.get_data()=3;
    cout<<a.get_data()<<endl; //data=3
    return 0;
}

那么这个时候为了避免成员函数被修改就需要加上const关键字,这个时候如果试图改变返回值是不允许的:

error: cannot assign to return value because function 'get_data' returns a const value

需要指出的是,如果函数的返回类型是内置类型,比如 int char 等,修改返回值本身就是不合法的!所以 const 返回值是处理返回类型为用户定义类型的情况。

const 修饰实参

多数情况下,我们都会选择 pass by reference,这样可以节省内存,并且可以起到改变实参的目的。不过有的时候我们并不希望改变实参的值,就要加上const关键字。

这个不仔细说了,很容易理解。不过在构造接口之前一定要思考函数是否会修改参数,如果不会修改的话一定要加上const,这个是代码写的是否大气的一个标准(侯捷大师原话。。)。

const成员函数

这种情况多数情形下很容易被忽视,其实这个是非常重要的一个内容。

设想这样一种场景:

const String str("hello world");
str.print();

假设在string类中有一个成员函数叫做print, 如果这个函数在定义的时候没有加上const 关键字,上述的代码是无法通过编译的,下面举个具体的例子:

#include<iostream>

using namespace std;

class A
{
private:
    int data;
public:
    A(int num):data(num){}
    ~A(){};
    int& get_data()
    {
        return data;
    }
};

int main()
{
    const A a(1);
    a.get_data();
    return 0;
}

毫不意外的出错了:

error: ‘this’ argument to member function ‘get_data’ has type ‘const A’, but function is not marked const。

我们敏锐的发现了一个“this"指针,这个从何说起?

其实任何成员函数的参数都是含有this 指针的,好比py中的 self ,只不过c++中规定全部不写这个参数, 其实这个参数就是对象本身, 即谁调用成员函数, 这个 this 就是谁!

我们的例子中 a 是一个const 对象, 它调用了 get_data 方法,所以函数签名应该是:

get_data(a){}

而 a是一个 const 对象, 我们默认的 this 指针并不是 const 类型,所以参数类型不匹配, 编译无法通过!

这下我们终于清楚了, const 修饰成员函数, 根本上是修饰了 this 指针。

下面摘取侯捷PPT中的一张图片,介绍了string源码中的一些片段:
这两个成员函数都重载了[]符号,而前者是const ,不必担心修改参数, 而后者由于引用计数的问题, 多个变量指向一个内存, 一旦修改就必须copy后修改,cow。
需要指出的是, 这俩函数确实是不同的函数, 加了这个const 之后函数签名是不一样的。

有如下的规则:
在这里插入图片描述
补充一点,如果成员函数同时具有 const 和 non-const 两个版本的话, const 对象只能调用const成员函数, non-const 对象只能调用 non-const 成员函数。

### C++ 中 `const` 修饰函数的用法及规则 在 C++ 中,当一个成员函数被声明为 `const` 时,表示该函数不会修改对象的状态。如果虚函数也需要满足这一特性,则可以通过将虚函数标记为 `const` 来实现。以下是关于 `const` 修饰函数的具体用法及其注意事项: #### 声明与定义 虚函数可以像普通成员函数一样被声明为 `const` 函数。这意味着它可以在常量对象上调用,并且不允许修改任何非静态数据成员。 ```cpp class Base { public: virtual void display() const { std::cout << "Base::display()" << std::endl; } }; ``` 上述代码中,`display()` 是一个虚函数并被声明为 `const`,因此它可以安全地用于常量对象上[^1]。 #### 继承中的行为 派生类覆盖基类的 `const` 虚函数时,也必须将其声明为 `const`。如果不这样做,编译器会认为这是两个不同的函数而非重载关系。 ```cpp class Derived : public Base { public: void display() const override { std::cout << "Derived::display()" << std::endl; } }; ``` 这里需要注意的是,在派生类中重新定义了一个具有相同签名(包括返回类型和参数列表)但带有 `const` 的版本来覆盖基类方法[^2]。 #### 返回类型的协变性 对于带 `const` 的虚函数来说,其返回类型同样遵循 C++ 的协变规则。即允许派生类改变返回类型为更具体的类型,只要它是原始返回类型的派生物即可。 ```cpp class Animal {}; class Dog : public Animal {}; class Base { public: virtual Animal* getAnimal() const = 0; }; class Derived : public Base { public: Dog* getAnimal() const override { return new Dog(); } }; ``` 在这个例子中展示了如何利用 covariant returns (协变返回),其中 `getAnimal()` 方法从返回泛型动物实例变为具体狗实例的同时保持了接口一致性[^3]。 #### 注意事项 - **不可更改状态**: 如果某个操作确实需要修改内部状态就不能标注成 `const` 类型;否则会导致错误。 - **隐式转换问题**: 当尝试通过非常量指针访问原本只适用于恒定情况下的功能时可能会遇到麻烦。 ```cpp void test(Base& obj){ obj.display(); // 此处可能引发警告或错误取决于实际运行环境设置 } int main(){ const Derived dObj; test(dObj); } ``` 上面这段程序试图把一个常数引用传递给接受可变量形参的方法里去调用后者所含有的非限定版别的同名显示指令,这通常不符合逻辑预期因而需谨慎处理此类情形[^4]。 --- ### 示例代码展示 下面给出一段完整的演示代码片段说明以上概念的应用场景: ```cpp #include <iostream> using namespace std; // 定义基类 class Shape { public: virtual double area() const = 0; // 抽象纯虚函数const属性 virtual ~Shape(){} }; // 圆形作为子类之一 class Circle : public Shape { private: double radius_; public: explicit Circle(double r=1.0):radius_(r){} double area() const override{ return 3.14 * radius_ * radius_; } }; // 矩形作为另一个子类 class Rectangle : public Shape { private: double width_, height_; public: Rectangle(double w=1.0,double h=1.0):width_(w),height_(h){} double area() const override{ return width_*height_; } }; int main(){ const Shape* shapes[]={ new Circle(5), new Rectangle(4,6) }; for(auto shape:shapes){ cout<<"Area:"<<shape->area()<<'\n'; } for(auto shape:shapes){ delete shape; } return 0; } ``` 此案例创建了一组不同几何图形的对象数组并通过统一接口计算各自面积值,充分体现了多态特性的优势所在。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值