9、C++ const关键字

参考资料:

【C++ const的各种用法详解】【const用法深入浅出】 - COS - 博客园 (cnblogs.com)

const的基本概念:

const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。

下面我就const的用法来谈谈:

const的用法大致可分为以下几个方面:

(1)const修饰基本数据类型

(2)const应用到函数中

(3)const在类中的用法

(4)const修饰类对象,定义常量对象 

一、const修饰基本数据类型 

1.const修饰一般常量及数组  

const int a=10;               等价的书写方式:      int const a=10;
const int arr[3]={1,2,3};                        int const arr[3]={1,2,3};

对于类似这些基本数据类型,修饰符const可以用在类型说明符前,也可以用在类型说明符后,其结果是一样的。

在使用这些常量的时候,只要不改变这些常量的值便好。  

 2.const修饰指针变量*及引用变量&

介绍本部分内容之前,先说说指针和引用的一些基本知识。

指针(pointer)是用来指向实际内存地址的变量,一般来说,指针是整型,而且一般的大家会接受十六进制的输出格式。

引用(reference)是其相应变量的别名,用于向函数提供直接访问参数(而不是参数的副本)的途径,与指针相比,引用是一种受限制的指针类型,或者说是指针的一个子集,而从其功能上来看,似乎可以说引用是指针功能的一种高层实现。

关于运算符&和*:
           在C++里,沿袭C中的语法,有两个一元运算符用于指针操作:&和*。按照本来的定义,&应当是取址符,*是指针符,也就是说, &用于返回变量的实际地址,*用于返回地址所指向的变量,他们应当互为逆运算。实际的情况也是如此。

在定义变量的引用的时候,&只是个定义引用的标志,不代表取地址。

举例:  

#include<iostream>
int main()
{
    int a;  //a is an integer
    int *aPtr;  //aPtr is a pointer to an integer
    a=7;
    aPtr = &a;
    std::cout<<"Showing that * and & are inverses of "<<"each other.\n";
    std::cout<<"a="<<a<<"  *aPtr="<<*aPtr<<"\n";
    std::cout<<"&*aPtr = "<<&*aPtr<<std::endl;//
    std::cout<<"*&aPtr = "<<*&aPtr <<std::endl;
    return 0;
}

在C++中,一个指针存储的是它所指向的变量的地址。因此,如果aPtr是一个指向a的指针,那么*aPtr将会是a的值,而&*aPtr将会是a的地址,即&a。同样,&aPtr将会是aPtr指针本身的地址。

在上述代码中,aPtr被赋值为&a,也就是a的地址。因此,&aPtr和&aPtr实际上是相同的,都是a的地址。

 了解完指针和应用的基本概念之后,下面继续我们的话题。

 const修饰指针(*):

const int* a = & [1]          //非常量数据的常量指针    指针常量
int const *a = & [2]          //非常量数据的常量指针     a is a pointer to the constant char variable
int* const a = & [3]          //常量数据的非常量指针指针常量 常量指针 a is a constant pointer to the (non-constant) char variable
const int* const a = & [4]    //常量数据的常量指针

 

 可以参考《Effective c++》Item21上的做法,

const int* a 和int const *a 都表示a是一个指向常量整型数的指针变量。它可以指向一个整型数,但是不能通过指针修改所指向的整型数的值

 如果const位于星号*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;

 如果const位于星号的右侧,const就是修饰指针本身即指针本身是常量

 因此,[1]和[2]的情况相同,都是指针所指向的内容为常量,这种情况下不允许对内容进行更改操作,如不能*a = 3 ;

[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;

[4]为指针本身和指向的内容均为常量。

 const修饰引用(&):

int const &a=x;
const int &a=x;
int &const a=x;//这种方式定义是C、C++编译器未定义,虽然不会报错,但是该句效果和int &a一样。   

 这两种定义方式是等价的,此时的引用a不能被更新。如:a++ 这是错误的

在使用 const 关键字定义引用变量时,实际上是定义了一个指向常量的引用,这意味着被引用的变量的值不能被修改

二、const应用到函数中 

 1.作为参数的const修饰符

 2.作为函数返回值的const修饰符  

 其实,不论是参数还是返回值,道理都是一样的,参数传入时候和函数返回的时候,初始化const变量

1      修饰参数的const,如 void fun0(const A* a ); void fun1(const A& a);

#include<iostream>
void foo(const int* ptr) {
    // 不能对ptr指向的数据进行修改
    // *ptr = 10;  // error: assignment of read-only location
}

void bar(const std::string& str) {
    // 不能对str进行修改
    // str += " world";  // error: passing ‘const std::string’ as ‘this’ argument discards qualifiers
}

int main() {
    int num = 5;
    std::string message = "hello";
    
    foo(&num);
    // bar(message);

    return 0;
}

在这个例子中,foo函数的参数ptr是一个指向int类型的指针,使用了const关键字修饰,表示该指针指向的数据不能被修改。如果在函数体内尝试修改ptr指向的数据,则会编译出错。同样的,bar函数的参数str是一个const引用类型的参数,也不能被修改。在main函数中,我们调用foobar函数时传递了相应的参数,使用了const关键字进行保护,避免了意外的数据修改。

另外,在定义一个返回值时使用const关键字也能够保护数据的不变性,例如:

class MyClass {
public:
    const int getValue() const {
        // 不能修改成员变量value的值
        // value = 10;  // error: assignment of member ‘MyClass::value’ in read-only object
        return value;
    }
    
private:
    int value = 5;
};

int main() {
    MyClass obj;
    int value = obj.getValue();
    // 不能修改getValue返回的值
    // value = 10;  // error: assignment of read-only variable ‘value’
    return 0;
}

在这个例子中,MyClass类中的getValue函数返回了一个const类型的整数,表示该函数返回的整数值不能被修改。在main函数中,我们调用了getValue函数,并将返回的整数值赋值给变量value,但是由于该整数值是const类型的,所以不能被修改。

类中的成员函数:A fun4()const; 其意义上是不能修改所在类的的任何变量。

 三、类中定义常量(const的特殊用法) 

在类中实现常量的定义大致有这么几种方式实现: 

1.使用枚举类型 

class test
{
     enum { SIZE1 = 10, SIZE2 = 20}; // 枚举常量
     int array1[SIZE1];  
     int array2[SIZE2];
};

 

2.使用const

不能在类声明中初始化const数据成员。以下用法是错误的,因为类的对象未被创建时,编译器不知道SIZE的值是什么。 

class test
  {
        const int SIZE = 100;     // 错误,企图在类声明中初始化const数据成员
        int array[SIZE];          // 错误,未知的SIZE
  };

正确的使用const实现方法为:const数据成员的初始化只能在类构造函数的初始化表中进行。 

class A
 {…
        A(int size);      // 构造函数
        const int SIZE ; 
 };
 A::A(int size) : SIZE(size)    // 构造函数的初始化表
{
      …
}
//error 赋值的方式是不行的
A::A(int size)
{
     SIZE=size;
}
void main()
{
    A  a(100); // 对象 a 的SIZE值为100
    A  b(200); // 对象 b 的SIZE值为200
}

注意:对const成员变量的初始化,不能在变量声明的地方,必须在类的构造函数的初始化列表中完成,即使是在构造函数内部赋值也是不行的。

3.使用static const 

通过结合静态变量来实现:

#include<iostream.h> 
class Year
{ 
private:
 int y; 
public:
 static int const Inity;
public: 
 Year()
 {
  y=Inity;
 }
 };
int const Year::Inity=1997;//静态变量的赋值方法,注意必须放在类外定义
void main()
{
 cout<<Year.Inity<<endl;//注意调用方式,这里是用类名调用的。

到这里就把在类中定义常量的方法都陈列出来了。 

四、const定义常量对象,以及常量对象的用法

下面是一个例子,展示如何定义和使用const对象变量

class MyClass {
public:
    void print() const {
        // 常量成员函数可以读取但不能修改数据成员
        // m_num = 100;  // error: assignment of member ‘MyClass::m_num’ in read-only object
        std::cout << "m_num = " << m_num << std::endl;
    }

private:
    int m_num = 0;
};

int main() {
    // 定义常量对象变量
    const MyClass obj1;
    obj1.print();

    // 定义对象变量
    MyClass obj2;
    obj2.print();

    return 0;
}

 

在这个例子中,我们定义了一个名为obj1的常量对象变量,并调用了它的成员函数print。由于obj1是常量对象,所以print函数不能修改m_num数据成员的值。

需要注意的是,定义const对象变量时,我们需要在对象名后面加上const关键字。如果一个类的成员函数声明中包含const关键字,那么该成员函数就是常量成员函数,表示该函数不能修改对象的状态。常量成员函数的语法格式如下:

返回类型 函数名() const;

 

在常量成员函数中,我们不能修改数据成员的值,但可以读取它们的值。常量成员函数通常用于保护数据成员的完整性,并提高程序的安全性。

五、使用const的一些建议

   <1>要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
   <2> 要避免最一般的赋值操作错误,如将const变量赋值,具体可见思考题;
   <3> 在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;
   <4> const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
   <5>不要轻易的将函数的返回值类型定为const;
   <6>除了重载操作符外一般不要将返回值类型定为对某个对象的const引用; 

 

 

 

1111111111111111111

posted @ 2023-08-21 15:46  多一些不为什么的坚持  阅读(83)  评论(0编辑  收藏  举报