关于常量成员函数,const用法小结
分类:高并发

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

常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,定义或说明常类型时必须进行初始化。

1什么是const?

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

概述

(const类型)常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。(但可以偷梁换柱进行更新)

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

  1. const有什么用途?

2为什么引入const?

例子:
class Coo
{
public:
Coo() : a(0)
{}
int getA() const //常量成员函数
{
++a; //编译错误
return this->a;
}
private:
mutable int a;
};

在 c程序中,const的用法主要有定义常量、修饰函数参数、修饰函数返回值等3个用处。

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。

getA() 后面的 const 表明 getA()不会修改类的成员变量,
但也不能确定,可以用mutable改变这一限制.

在c++程序中,它还可以修饰函数的定义体,定义类中某个成员为常态函数,即不改变类中的数据成员。

3主要作用

用const结尾的成员函数只能调用其他const结尾的成员函数。因为其他函数并不能保证同样不修改类的成员变量

被const修改的东西都要受到强制保护,可以预防意外的变动,能提高程序的健壮性。

(1)可以定义const常量,具有不可变性。 

成员函数后加const,表示:类作者提示你,这个函数不会导致对象状态改变
可以理解为"表示这个函数不会修改任何成员变量"
但不可绝对化。其实,还是可以修改由mutable关键字修饰的成员变量的。

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

例如:const int Max=100; int Array[Max]; 

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

(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。

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

例如: void f(const int i) { .........} 编译器就会知道i是一个常量,不允许修改; 

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

(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。同宏定义一样,可以做到不变则已,一变都变!

 

如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可! 

 

(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。还是上面的例子,如果在函数体内修改了i,编译器就会报错; 

C++中的Const

例如: void f(const int i) { i=10;//error! } 

 

(5)可以节省空间,避免不必要的内存分配。例如: 

一,一般常量和对象常量

#define PI 3.14159 //常量宏 

  1. 一般常量
  一般常量是指简单类型的常量。这种常量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后。如:
  int const x=2;
  或
  const int x=2;
  定义或说明一个常数组可采用如下格式:
  <类型说明符> const <数组名>[<大小>]…
  或者
  const <类型说明符> <数组名>[<大小>]…
  例如:
  int const a[5]={1, 2, 3, 4, 5};

const double Pi=3.14159; //此时并未将Pi放入RAM中 ...... 

  2. 常对象
  常对象是指对象常量,定义格式如下:
  <类名> const <对象名>
  或者
  const <类名> <对象名>
  定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符const可以放在类名后面,也可以放在类名前面。

double i=Pi; //此时为Pi分配内存,以后不再分配! 

二,常指针和常引用

double I=PI; //编译期间进行宏替换,分配内存 

  1. 常指针
  使用const修饰指针时,由于const的位置不同,而含意不同。如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。(如果const在*的左边,则被指物为常量;在右边,则指针为常量)下面举两个例子,说明它们的区别。
  下面定义的一个指向字符串的常量指针:
  char * const prt1 = stringprt1;
  其中,ptr1是一个常量指针。因此,下面赋值是非法的。
  ptr1 = stringprt2;
  而下面的赋值是合法的:
  *ptr1 = "m";
  因为指针ptr1所指向的变量是可以更新的,不可更新的是常量指针ptr1所指的方向(别的字符串)。
  下面定义了一个指向字符串常量的指针:
  const * ptr2 = stringprt1;
  其中,ptr2是一个指向字符串常量的指针。ptr2所指向的字符串不能更新的,而ptr2是可以更新的。因此,
  *ptr2 = "x";
  是非法的,而:
  ptr2 = stringptr2;
  是合法的。
  所以,在使用const修饰指针时,应该注意const的位置。定义一个指向字符串的指针常量和定义一个指向字符串常量的指针时,const修饰符的位置不同,前者const放在*和指针名之间,后者const放在类型说明符前。

double j=Pi; //没有内存分配 

  2. 常引用
  使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:
  const <类型说明符> & <引用名>
  例如:
  const double & v;
  在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。

double J=PI; //再进行宏替换,又一次分配内存! 

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

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。 

  下面举一例子说明常指针作函数参数的作法。

(6)提高了效率。 

[cpp] view plaincopyprint?

编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. const int n = 6;   
  5. void print(const int *p, int n);   
  6.   
  7. void main() {   
  8.     int array[n];           //数组定义必须使用常量  
  9.     for (int i=0; i<n;i++)  //输入六个数  
  10.         cin>>array[i];   
  11.     print(array, n);        //输出六个数  
  12. }   
  13. void print(const int *p, int n)   //表示p指向的内容不可修改,p本身可以修改  
  14. {   
  15.     cout<<"{"<<*p;   
  16.     for (int i=1; i<n;i++)  
  17.         cout<<","<<*(p+i);   
  18.     cout<<"}"<<endl;  
  19. }   

4 const怎样限定内容

三,常成员函数
  使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:
  <类型说明符> <函数名> (<参数表>) const;

限定变量时:

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

例:下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?

[cpp] view plaincopyprint?

typedef char * pStr;

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class r  
  5. {  
  6. public:  
  7.     r(int a, int b):r1(a),r2(b) {}  
  8.     void print();  
  9.     void print() const;  
  10. private:  
  11.     int r1, r2;  
  12. };  
  13.   
  14. void r::print()  
  15. {  
  16.     cout<<r1<<","<<r2<<endl;  
  17. }  
  18.   
  19. void r::print() const   
  20. {  
  21.     cout<<"const: "<<r1<<","<<r2<<endl;  
  22. }  
  23.   
  24. void main()  
  25. {  
  26.     r a(5, 4);  
  27.     a.print();  
  28.     const r b(20, 52);  
  29.     b.print();  
  30. }  

char string[4] = "bbc";

该例子的输出结果为:  

const char *p1 = string; //1式

5,4  20;52  

const pStr p2 = string; //2式

该程序的类声明了两个成员函数,其类型是不同的(其实就是重载成员函数)。

p1++;

有带const修饰符的成员函数处理const常量,这也体现出函数重载的特点。

p2++;

四,常数据成员  

答案与分析:

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

问题出在p2++上。

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

const使用的基本形式: const type m;限定m不可变。替换基本形式中的m为1式中的*p1,替换后const char *p1;限定*p1不可变,当然p1是可变的,因此问题中p1++是对的。替换基本形式中的type为2式中的pStr,替换后const pStr m;限定m不可变,题中的pStr就是一种新类型,因此问题中p2不可[1]变,p2++是错误的。

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

const只修饰其后的变量,至于const放在类型前还是类型后并没有区别。如:const int a和int const a都是修饰a为const。一个简单的判断方法:指针运算符*,是从右到左,那么如:char const * pContent,可以理解为char const (* pContent),即* pContent为const,而pContent则是可变的。

[cpp] view plaincopyprint?

int const * p1,p2;

  1. #include <iostream>  
  2. using namespace std;  
  3. class T  
  4. {  
  5. public:  
  6.     T(int i);  
  7.     void print();  
  8.     const int &r;  
  9. private:  
  10.     const int a;  
  11.     static const int b=10;//静态常量初始化可以在类内,也可以在类外  
  12. };  
  13. //const int T::b=20; //也可以在类外,但是只能初始化一次  

p2是const;(*p1)是一整体,因此(*p1)是const,但p1是可变的。int * p1,p2只代表p1是指向整型的指针,要表示p1、p2都是指针是需写成int * p1,* p2。所以无论是* const p1,p2还是const * p1,p2,里面的*都是属于p1的。

[cpp] view plaincopyprint?

int const * const p1,p2;

  1. T::T(int i):a(i),r(a)          //必须在初始化列表中给常量初始化,但是不能初始化静态常量  
  2. {  
  3.     //a=i; //这里不可以  
  4. }  
  5. void T::print()  
  6. {  
  7.     cout<<r<<":"<<b<<":"<<a<<endl;  
  8. }  
  9. void main()  
  10. {  
  11.     T a1(100), a2(0);  
  12.     a1.print();  
  13.     a2.print();  
  14. }  

p2是const,是前一个const修饰的,*p1也被前一个const修饰,而p1被后一个const修饰。

  该程序的运行结果为:
  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都是常类型的,需要采用初始化格式

int * const p1,p2;

转自:

p1是const,(* const p1)是整体,所以const不修饰p2。

指针指向及其指向变量的值的变化

const在*的左边,则指针指向的变量的值不可变;在*的右边,则指针的指向不可变。简记为"左定值,右定向"。

1)指针指向的变量的值不能变,指向可变

int x = 1;

int y = 2;

const int* px = &x;

int const* px = &x; //这两句表达式一样效果

px = &y; //正确,允许改变指向

*px = 3; //错误,不允许改变指针指向的变量的值

2)指针指向的变量的值可以改变,指向不可变

int x = 1;

int y = 2;

int* const px = &x;

px = &y; //错误,不允许改变指针指向

*px = 3; //正确,允许改变指针指向的变量的值

3)指针指向的变量的值不可变,指向不可变

int x = 1;

int y = 2;

const int* const px = &x;

int const* const px = &x;

px = &y; //错误,不允许改变指针指向

*px = 3; //错误,不允许改变指针指向的变量的值

以 const 声明的变量的指针只能分配给同时声明为 const的指针。

// constant_values4.cpp
#include <stdio.h>
int main() {
const char *mybuf = "test";
char *yourbuf = "test2";
printf_s("%sn", mybuf);

const char *bptr = mybuf; // Pointer to constant data
printf_s("%sn", bptr);

// *bptr = 'a'; // Error
}

不能声明具有 const 关键字的构造函数或析构函数。

   

限定成员函数时:

声明带有 const 关键字的成员函数指定,函数是 "只读"函数,在它被调用的时候不会修改对象。 一个常数成员函数不能修改任何非静态数据成员或调用不是常数的任何成员函数。若要声明常数成员函数,请在参数列表的右括号后放置const关键字,把const关键字放在函数的参数表和函数体之间。有人可能会问:为什么不将const放在函数声明前呢?因为这样做意味着函数的返回值是常量,意义完全不同。 声明和定义中均要求该 const 关键字。

// constant_member_function.cpp
class Date
{
public:
Date( int mn, int dy, int yr );
int getMonth() const; // A read-only function
void setMonth( int mn ); // A write function; can't be const
private:
int month;
};

int Date::getMonth() const
{
return month; // Doesn't modify anything
}
void Date::setMonth( int mn )
{
month = mn; // Modifies data member
}
int main()
{
Date MyDate( 7, 4, 1998 );
const Date BirthDate( 1, 18, 1953 );
MyDate.setMonth( 4 ); // Okay
BirthDate.getMonth(); // Okay
BirthDate.setMonth( 4 ); // C2662 Error
}

限定函数的传递值参数:

void function(const int Var); //传递过来的参数在函数内不可以改变.

限定函数返回值型.

const int function(); //此时const无意义

const myclassname function(); //函数返回自定义类型myclassname.

限定函数类型.

void function()const; //常成员函数, 常成员函数是不能改变成员变量值的函数。

非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员. 任何不修改成员数据的函数都应该声明为const函数,这样有助于提高程序的可读性和可靠性。

void AddElem(const int);

bool Set::Member (const int elem) const;

const Set s;

s.AddElem(10); // 非法: AddElem不是常量成员函数

s.Member(10); // 正确

构造函数和析构函数对这个规则例外,它们从不定义为常量成员,但可被常量对象调用(被自动调用)。它们也能给常量的数据成员赋值,除非数据成员本身是常量。

   

   

const成员函数和const对象

实际上,const成员函数还有另外一项作用,即常量对象相关。对于内置的数据类型,我们可以定义它们的常量,用户自定义的类也一样,可以定义它们的常量对象。例如,定义一个整型常量的方法为:

const int i=1 ;

同样,也可以定义常量对象,假定有一个类classA,定义该类的常量对象的方法为:

const classA a(2);

这里,a是类classA的一个const对象,"2"传给它的构造函数参数。const对象的数据成员在对象寿命期内不能改变。但是,如何保证该类的数据成员不被改变呢?

为了确保const对象的数据成员不会被改变,在C++中,const对象只能调用const成员函数。如果一个成员函数实际上没有对数据成员作任何形式的修改,但是它没有被const关键字限定的,也不能被常量对象调用。下面通过一个例子来说明这个问题:

class C

{

int X;

public:

int GetX()

{

return X;

}

void SetX(int X)

{

this->X = X;

}

};

void main()

{

const C constC;

cout<<constC.GetX();

}

如果我们编译上面的程序代码,编译器会出现错误提示:constC是个常量对象,它只能调用const成员函数。虽然GetX( )函数实际上并没有改变数据成员X,由于没有const关键字限定,所以仍旧不能被constC对象调用。如果我们将上述代码中:

int GetX()

改写成:

int GetX()const

再重新编译,就没有问题了。

来自 <>

来自 <>

来自 <>

来自 <>

来自 <>

来自 <>

本文由10bet手机官网发布于高并发,转载请注明出处:关于常量成员函数,const用法小结

上一篇:在django中实现类似rails的binding,ipython比pry好用的地方 下一篇:没有了
猜你喜欢
热门排行
精彩图文