关于C++:各种常量

关于C++:各种常量

本小白了解C++中const关键字的用法,特在此做个笔记
今天在助教工作中遇到同学提出的一些问题,认为非常有意义且重要,特此来整理一下所学

const在面向过程编程时的用法:

修饰基本变量使之成为不可改动的常量:

#include <iostream>
using namespace std;
int main() {
    const int a = 5;
    // a=10;//此句编译不通过,错误提示:表达式必须是可修改的左值
    cout << a;
    return 0;
}

输出:

5

这种用法必须在定义变量时初始化,而不可以在之后赋初值:

    const int a;//错误:常量 变量 "a" 需要初始值设定项
    a = 10; //错误:表达式必须是可修改的左值

修饰指针:

#include <iostream>
using namespace std;
int main(void) {
    int num;
    const int *p1 = &num;
    // p1可变,*p1不可变,此时不能用*p1来修改,但是p1可以转向
    int *const p2 = &num;
    // p2不可变,*p2可变,此时允许*p2来修改其值,但是p2不能转向。
    const int *const p3 = &num;
    // p3不可变,*p3也不可变,此时既不能用*p3来修改其值,也不能转向
    return 0;
}

修饰函数返回值:

事实上,修饰函数返回值并非没有什么卵用:

非指针返回值:

对非指针返回值确实没有什么卵用:

#include <iostream>
using namespace std;
const int fun(int a){
    const int b=a;
    return b;
}
int main() {
    int b=fun(10);
    b=100;
    cout<<b;
    return 0;
}

输出:

100

为啥呢,实际上函数的调用这个式子本身就是个常量:

#include <iostream>
using namespace std;
const int fun1(int a){
    const int b=a;
    return b;
}
int fun2(int a){
    const int b=a;
    return b;
}
int main() {
    int b=fun1(10)=100;//错误:表达式必须是可修改的左值
    int b=fun2(10)=100;//错误:表达式必须是可修改的左值
    cout<<b;
    return 0;
}

这里使用了连续赋值从右到左依次赋值的语法,可以见到,fun2(10)=100本身就不通过。

也对,人家函数辛辛苦苦得到的结果,凭什么能让你随便改。

指针返回值:

修饰指针指向内存地址也和上面一样没什么卵用:

#include <iostream>
using namespace std;
int *const fun(int a) {
    int *const pa = new int(a);
    return pa;
}
int main() {
    int *b = fun(10);
    *b = 100;
    cout << *b;
    return 0;
}

输出:

100

但是修饰指向值就有用起来了:

#include <iostream>
using namespace std;
const int * fun(int a) {
    const int* pa = new int(a);
    return pa;
}
int main() {
    int *b = fun(10);//错误:"const int *" 类型的值不能用于初始化 "int *" 类型的实体
    *b=100;
    cout << *b;
    return 0;
}

这就让我们不能偷鸡摸狗的通过返回的地址瞎搞了。

修饰引用:

void fun(testclass &a){
    a.testvar3+=5;//正确
}
void fun(testclass const &a){
    a.testvar3+=5;//错误:表达式必须是可修改的左值
}

可以见到,const修饰的引用不可以进行更改。

const在面向对象编程时的用法:

首先是实现个实验类:

#include <iostream>
using namespace std;
class testclass {
public:
    string name;
    const int testvar1;
    const int testvar2 = 5;
    int testvar3;
    testclass(string in_name, int in_1, int in_2 = 20)
        : name(in_name), testvar1(in_1), testvar3(in_2) {}
    void output(void) {
        cout << "<" << name << " is a general member>\t"
             << "var1:" << testvar1 << ' ' << "var2:" << testvar2 << ' '
             << "var3:" << testvar3 << endl;
    }
    void output(void) const {//这写法一会再说
        cout << "<" << name << " is a const member>\t"
             << "var1:" << testvar1 << ' ' << "var2:" << testvar2 << ' '
             << "var3:" << testvar3 << endl;
    }
};
int main(void) {
    testclass class_a("class_a", 10);
    const testclass class_b("class_b", 20);
    class_a.output();
    class_b.output();
    return 0;
}

输出:

<class_a is a general member>   var1:10 var2:5 var3:20
<class_b is a const member>     var1:20 var2:5 var3:20

完美,接下来看——

常成员:

如下的两句定义了两个常成员变量:

    const int testvar1;
    const int testvar2 = 5;

常成员变量无法在初始化后被修改:

    class_a.testvar1=20;//错误:表达式必须是可修改的左值
    class_a.testvar2=10;//错误:表达式必须是可修改的左值

而类的常成员变量如何初始化呢?请看类内的代码:

    testclass(string in_name, int in_1, int in_2 = 20)
        : name(in_name), testvar1(in_1), testvar3(in_2) {}

我们使用了构造函数的初始化列表以初始化常成员变量,而不是在构造函数内,这意味着构造函数的确是先建立变量的内存空间,在执行构造函数的语句块。
而使用初始化列表可以初始化常成员变量,这说明了初始化列表在执行构造函数的语句块前执行,即在建立变量的同时。
好,我们搞明白了常成员变量,那么常成员函数呢?
请想一下,“常”指的是什么?是常量。那么函数是啥,是操作。
那么常成员函数又是什么应该不难想到,就是不修改数据的操作呗,来看:

    void change(int in_) const { testvar3 = in_; }//错误:表达式必须是可修改的左值

testvar3明明声明是一个变量,为森马会提示说它不能改呢?
原因在于const修饰的成员函数把所有的成员变量都视为常量。
在这里,const关键字是函数类型的一个组成部分,因此在实现部分也要带关键字:

    void fun(int in_) const;         //声明
    void fun(int in_) const {...}    //实现

是故const关键字可以用于对重载函数的区分。

常对象:

如下的一句声明了一个常对象:

    const testclass class_b("class_b", 20);

对于常对象,我们无法对他的值进行修改,

    class_b.testvar3=6;//错误:表达式必须是可修改的左值

对于非常量对象,可以调用它的常量与非常量函数,但对于常量对象,只能调用它的常成员函数:

    testclass class_a("class_a", 10);
    const testclass class_b("class_b", 20);
    class_a.output();
    class_b.output();

输出:

<class_a is a general member>   var1:10 var2:5 var3:20
<class_b is a const member>     var1:20 var2:5 var3:20

可以看到我们output函数有两个重载:

    void output(void) {
        cout << "<" << name << " is a general member>\t"
             << "var1:" << testvar1 << ' ' << "var2:" << testvar2 << ' '
             << "var3:" << testvar3 << endl;
    }
    void output(void) const {
        cout << "<" << name << " is a const member>\t"
             << "var1:" << testvar1 << ' ' << "var2:" << testvar2 << ' '
             << "var3:" << testvar3 << endl;
    }

对于非常量对象,我们可以运行他的两种函数,但对与以上这种重载,我们优先运行他的非常量版本,而对于常对象,我们只能运行他的常量成员函数。

觉得还行的看官点个攒再走吧…

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值