首页 > 技术文章 > c/c++ const 用法

cvtoEyes 2018-03-28 18:03 原文

概述

1. const有什么用途?

  • 在 c程序中,const的用法主要有定义常量、修饰函数参数、修饰函数返回值等3个用处。 
  • 在c++程序中,它还可以修饰函数的定义体,定义类中某个成员为常态函数,即不改变类中的数据成员。 
  • 被const修改的东西都要受到强制保护,可以预防意外的变动,能提高程序的健壮性。

2. const与#define相比有什么不同?

它们都可以用来定义常量,但const比#define有更多优点:

(1) const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意料不到的错误(边际效应)。

(2) 在c++中使用const常量而不使用宏常量,即const常量完全取代宏常量。

C++中的const

一、一般常量和对象常量

1. 一般常量

  一般常量是指简单类型的常量。这种常量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后。如:

int const x=2;
  或
  const int x=2;

定义或说明一个常数组可采用如下格式:

<类型说明符> const <数组名>[<大小>]…
//或者
const <类型说明符> <数组名>[<大小>]…
//例如:
  int const a[5]={1, 2, 3, 4, 5};

2. 常对象

  常对象是指对象常量,定义格式如下:

<类名> const <对象名>
//或者
const <类名> <对象名>

 定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符const可以放在类名后面,也可以放在类名前面。

二,常指针和常引用

1. 常指针

  使用const修饰指针时,由于const的位置不同,而含意不同。如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。下面举两个例子,说明它们的区别。 
   
  下面定义的一个指向字符串的常量指针: 

 char * const prt1 = stringprt1; 

  其中,ptr1是一个常量指针。因此,下面赋值是非法的。 

 ptr1 = stringprt2; 

  而下面的赋值是合法的: 

  *ptr1 = "m";

  下面定义了一个指向字符串常量的指针: 

 const * ptr2 = stringprt1; 

  其中,ptr2是一个指向字符串常量的指针。ptr2所指向的字符串不能更新的,而ptr2是可以更新的。因此, 

  *ptr2 = "x"; 

  是非法的,而: 

 ptr2 = stringptr2; 

  是合法的。 
  所以,在使用const修饰指针时,应该注意const的位置。定义一个指向字符串的指针常量和定义一个指向字符串常量的指针时,const修饰符的位置不同,前者const放在*和指针名之间,后者const放在类型说明符前。

2. 常引用

  使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下: 

 const <类型说明符> & <引用名> 

  例如: 

 const double & v; 

  在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。 
  在c++面向对象的程序设计中,指针和引用使用得较多,其中使用const修饰的常指针和常引用用得更多。使用常参数则表明该函数不会更新某个参数所指向或所引用的对象,这样,在参数传递过程中就不需要执行拷贝初始化构造函数,这将会改善程序的运行效率。 
  下面举一例子说明常指针作函数参数的作法。

#include <iostream>
using namespace std;

const int n = 6; 
void print(const int *p, int n); 

void main() { 
    int array[n];           //数组定义必须使用常量
    for (int i=0; i<n;i++)  //输入六个数
        cin>>array[i]; 
    print(array, n);        //输出六个数
} 
void print(const int *p, int n)   //表示p指向的内容不可修改,p本身可以修改
{ 
    cout<<"{"<<*p; 
    for (int i=1; i<n;i++)
        cout<<","<<*(p+i); 
    cout<<"}"<<endl;
} 

三,常成员函数

  使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下: 

<类型说明符> <函数名> (<参数表>) const

本质: 成员函数的第一个参数是const this指针,const修饰的成员函数的第一个参数是const T *this。 
其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。下面举一例子说明常成员函数的特征。

#include <iostream>
using namespace std;

class r
{
public:
    r(int a, int b):r1(a),r2(b) {}
    void print();
    void print() const;
private:
    int r1, r2;
};

void r::print()
{
    cout<<r1<<","<<r2<<endl;
}

void r::print() const 
{
    cout<<"const: "<<r1<<","<<r2<<endl;
}

void main()
{
    r a(5, 4);
    a.print();
    const r b(20, 52);
    b.print();
}

该例子的输出结果为: 5,4  const:20,52  

该程序的类声明了两个成员函数,其类型是不同的(其实就是重载成员函数)。 
有带const修饰符的成员函数处理const常量,这也体现出函数重载的特点。

四,常数据成员  

类型修饰符const不仅可以说明成员函数,也可以说明数据成员。  

由于const类型对象必须被初始化,并且不能更新,因此,在类中说明了const数据成员时,只能通过成员初始化列表的方式来生成构造函数对数据成员初始化。  

下面通过一个例子讲述使用成员初始化列表来生成构造函数。

#include <iostream>
using namespace std;
class T
{
public:
    T(int i);
    void print();
    const int &r;
private:
    const int a;
    static const int b=10;//静态常量初始化可以在类内,也可以在类外
};
//const int T::b=20; //也可以在类外,但是只能初始化一次
T::T(int i):a(i),r(a)          //必须在初始化列表中给常量初始化,但是不能初始化静态常量
{
    //a=i; //这里不可以
}
void T::print()
{
    cout<<r<<":"<<b<<":"<<a<<endl;
}
void main()
{
    T a1(100), a2(0);
    a1.print();
    a2.print();
}

该程序的运行结果为:100:10:100  0:10:0 
在该程序中,说明了如下三个常类型数据成员: 

  const int & r; 
  const int a; 
  static const int b; 

其中,r是常int型引用,a是常int型变量,b是静态常int型变量。程序中对静态数据成员b进行初始化。值得注意的是构造函数的格式如下所示:

a(int i):a(i),r(a)
  {
  }

其中,冒号后边是一个数据成员初始化列表,它包含两个初始化项,用逗号进行了分隔,因为数据成员a和r都是常类型的,需要采用初始化格式。

本文转自:博客

推荐阅读