C++ Const 初步总结(《C++程序设计语言》读后感)

本文深入探讨C++中常量引用的特性,包括其在函数参数中的应用、与指针的区别,以及如何正确理解和使用const引用。通过具体代码示例,解释了const引用在不同上下文中的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工作已经进入到了11年的工龄了。在我这个年龄大部分人都去做管理岗位了。

对于我来说, 还是忠于编程,编程就是我的兴趣爱好。干一行就爱一行。

开始学习C++的基础知识。在看《C++程序设计语言》的时候,看到引用这章节的介绍时,里面有这么一段代码:

template<class T>
class vector {
    T* elem;
    // ...
public:
    T& operator[](int i) { return elem[i]; }
    const T& operator[](int i) const { return elem[i]; }

    void push_back(const T& a);
    //...
};

void f(const vector<double>& v)
{
    double d1 = v[1];
    v[2] = 7;    //set 7 to v.operator[](2) indirect double

    v.push_back(d1);
}

在f函数里面, v[2]被设置成了值7,这里就很困惑。vector不是被设定成了常量吗?怎么可以改变引用对象的值呢?颠覆了我的三观。

看来需要重新复习常量的基础知识了。

一:const修饰成员变量

从基础介绍中了解:const大致意思是“我承诺不改变这个值”。主要用于说明接口,这样在把变量传入函数时就不必担心变量会在函数内被改变了。编译器负责确认并执行const的承诺。一旦我们把某物声明成const,就确保它的值在其作用域内不会发生改变。

对于指针这个类型定义的常量来看。我们要了解指针的一些信息。一个指针牵扯到两个对象,指针本身以及指针所指的对象。所以存在两种指针类型的常量。

1,在指针的声明语句中“前置”const关键字将令所指的对象而非指针本身成为常量。

2,在指针的声明语句中“后置”const关键字将令指针本身成为常量。

如下:

#include<iostream>
using namespace std;
int main(int arc, char **argv){
    int a1=3;   ///non-const data
    const int a2=a1;    ///const data

    int * a3 = &a1;   ///non-const data,non-const pointer
    const int * a4 = &a1;   ///const data,non-const pointer
    int * const a5 = &a1;   ///non-const data,const pointer
    int const * const a6 = &a1;   ///const data,const pointer
    const int * const a7 = &a1;   ///const data,const pointer

    return 0;
}

这是一些有关常用指针的sample。声明运算符 *const的作用是令指针本身成为常量。C++允许把非const变量的地址赋给指向常量的指针,不允许把常量的地址赋给某个不受限的指针。

介绍到这里,我们返回到刚开始写的常量引用是怎么回事?那么还是需要首先理解引用的本质。

引用是一种C++的语言机制,和指针类似,作为对象的别名存放对象的机器地址。与指针相比,引用不会带来额外的开销。

二者之间的区别:

  • 访问引用与访问对象本身从语法形式上看是一样的。
  • 引用所引的永远是一开始初始化的那个对象
  • 不存在“空引用”,我们可以认为引用一定对应着某个对象

引用最重要的用途是作为函数的实参或者返回值。

存在三种形式的引用:

左值引用(lvalue reference):引用那些我们希望改变值的对象。

const引用(const reference):引用那些我们不希望改变值的对象(比如常量)

右值引用(rvalue reference):所引用对象的值在我们使用之后就无须保留了(比如临时变量)

这三种形式统称为引用,其中前两种形式都是左值引用

 

引用的实现方式类似于常量指针,每次使用引用实际上是对该指针执行解引用操作。(这种只是辅助理解,引用不是对象,指针式一种对象)。

回到了引发这边博文的核心问题点,const类型的引用。《C++程序设计语言》【第四版】中的第165中有这么一段话:

const T& 的初始值不一定非得是左值,甚至可以不是T类型的。此时:

  • 首先,如果必要的话先执行目标为T的隐式类型转换
  • 然后,所得的值置于一个T类型的临时变量中
  • 最后,把这个临时变量作为初始值

事咧:

double & d1 = 1;              //错误:此处需要左值

const double& cdr {1};   //OK

const的这条语句的初始化过程可以理解为:

double temp = double{1};      //首先用给定的值创建一个临时变量

const double& cdr {temp};    //然后用这个临时变量作为cdr的初始值

用于存放引用初始值的临时变量的生命周期从它创建之处开始,到它的引用作用域结束为止。


我写了一段测试代码如下:

#include <iostream>
  
using namespace std;
void display(int const &ref) {
    ref = 7;  //对常量引用ref进行重新赋值
    cout << ref << '\n';
}

int main() {
    int i = 1;
    display(i);
    int const anotheri = 2;
    display(anotheri);
    display(2);
    display(1 + 2);
    display(static_cast<int>(3.14159));
}

编译结果如下:

medeadeMacBook-Pro:C++ medea$ g++ const_test.c -o const_test
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]
const_test.c:5:9: error: cannot assign to variable 'ref' with const-qualified type 'const int &'
    ref = 7;
    ~~~ ^
const_test.c:4:25: note: variable 'ref' declared const here
void display(int const &ref) {
             ~~~~~~~~~~~^~~
1 error generated.

可以看到常量引用是不能赋值的。那么回到最初代码进行再次分析:

    T& operator[](int i) { return elem[i]; }
    const T& operator[](int i) const { return elem[i]; }

以上可以看到其实对操作符[],有两个重载函数。调用的代码如下:

void f(const vector<double>& v)
{
    double d1 = v[1];
    v[2] = 7;    //set 7 to v.operator[](2) indirect double

    v.push_back(d1);
}

对于v[2] = 7,首先执行的是是操作符号[]。那么到底匹配那个重载函数呢?我们用事实来证明,参照下面的代码:

#include <iostream>
  
template<class T>
class vector {
    T* elem;
    // ...
public:
    T& operator[](int i) { return elem[i]; }
    const T& operator[](int i) const { return elem[i]; }

};

void f(const vector<double>& v)
{
    double d1 = v[1];
    v[2] = 7;    //set 7 to v.operator[](2) indirect double
}

int main(int argc, char **argv)
{
    vector<double> test;

    f(test);
    return 0;
}

编译结果如下:

medeadeMacBook-Pro:C++ medea$ g++ const_vector.c -o const_vector
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]
const_vector.c:16:10: error: cannot assign to return value because function 'operator[]' returns a const value
    v[2] = 7;    //set 7 to v.operator[](2) indirect double
    ~~~~ ^
const_vector.c:9:11: note: function 'operator[]' which returns const-qualified type 'const double &' declared here
    const T& operator[](int i) const { return elem[i]; }
          ^~
1 error generated.

也是编译不过。 难道不能重载。

把const T& operator[](int i) const { return elem[i]; }注释掉后,继续编译结果如下:

medeadeMacBook-Pro:C++ medea$ g++ const_vector.c -o const_vector
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]
const_vector.c:15:18: error: no viable overloaded operator[] for type 'const vector<double>'
    double d1 = v[1];
                ~^~
const_vector.c:8:8: note: candidate function not viable: 'this' argument has type 'const vector<double>', but method is not marked const
    T& operator[](int i) { return elem[i]; }
       ^
const_vector.c:16:6: error: no viable overloaded operator[] for type 'const vector<double>'
    v[2] = 7;    //set 7 to v.operator[](2) indirect double
    ~^~
const_vector.c:8:8: note: candidate function not viable: 'this' argument has type 'const vector<double>', but method is not marked const
    T& operator[](int i) { return elem[i]; }
       ^
2 errors generated.

通过上面两次实验,可以知道。首先:

1,main函数中调用f函数,将test对象复制给常量引用const vector<double>& v。

2,由于形参是常量引用,所以调用的函数也必须是const类型。当调用操作符[]的时候,需要匹配const T& operator[](int i) const 函数。

 

综上所诉,这里应该是书本上写错了。至少是翻译的中文版本是写错了。英文版本有时间在去看看。

到这里const引用的语言机制(编译器行为)已经比较清晰。但是又引发出来了一个新的问题:

引用类似于常量指针,那么const引用从字面上面理解应该代表的是引用住对象的不变性。如果按照上面的语言机制,const引用是可以写入值的,使用一个临时变量保存备份,有什么特殊意义?

这个问题需要答案,期待阅读这篇文章的大牛们帮忙解惑。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值