c++ const 限定符

c++ const 限定符

1.const引用与非const引用

const引用的含义是指向const对象的引用,非const引用表示指向非const类型的引用。

非const引用不可以绑定到const对象,例如:

const int iSize = 1024;

int &iRef = iSize;//error

将提示错误:

prog4.cpp(8) : error C2440: “初始化”: 无法从“const int”转换为“int &”

        转换丢失限定符

非const引用只能绑定到同类型的非const对象(因为非const引用可以修改绑定的对象,如果绑定到const对象,那么又不能通过这个引用改变这个对象,这不是自相矛盾,所以不能绑定到const对象),const引用则可以绑定到不同但相关的类型的对象或者右值

绑定到不同但相关类型对象,例如:

#include <iostream>

//const引用绑定到不同但相关类型的对象

int main()

{

double dVal = 3.14;

const int &ri = dVal;

//int &ref = dVal;//error,无法从“double”转换为“int &”

std::cout<<"ri:"<<ri<<std::endl;//print 3

std::cout<<"dVal:"<<dVal<<std::endl;//print 3.14

return 0;

}



这里ri绑定到dVal并不影响dVal本身,只是编译器构造了临时变量,即实际效果等价于代码:

Int tmp = dVal;//cast to int

const int &ri = tmp;//bind to temporary variable

 

2.指针和const限定符

1)指向const对象的指针——不能用来修改其指向的对象的值

一般使用的指针,允许我们通过指针修改其指向对象的值。如果指针指向的是const对象,则不允许用指针来修改其所指对象的值,这就需要将指针定义为指向const对象的指针。指向const对象的指针确保不能用来修改基础对象。

注意,指向const对象的指针,不允许修改其指向的对象的值,但是允许给指针重新赋值从而指向另一个const对象。同时注意,不能使用void*保存const对象的地址,必须使用const void*保存。

在实际应用中,指向const的指针常用作函数的形参,以确保传递给函数的实际对象在函数中不因为形参而被修改。

2)const指针——本身的值不可修改

const指针的本身的值不可修改,但是并没有说明能否使用该指针修改它指向对象的值。指针所指对象的值能否修改完全取决于该对象的类型。

3)指向const对象的const指针——本身和所指向对象的值均不能改变

  这种类型的指针,一经定义就不能做任何更改,既不能改变指向,也不能改变其指向对象的值。例如:

const double pi = 3.14;

const double* const pi_ptr = π

从右至左阅读,解释为pi_ptr首先是一个const指针,指向double类型的const对象。当然,这里确实非常混淆。

const限定符和指针,可参见下例:

#include <iostream>

//指向const对象的指针和const指针

int main()

{   

const double pi = 3.14;

double exp = 2.718;

const double *cptr;//指向const对象的指针,在定义时不强制初始化

cptr = π

std::cout<<*cptr<<std::endl;

cptr = &exp;//允许重新绑定到另一个const对象

std::cout<<*cptr<<std::endl;

//double *ptr = π//错误,无法从“const double *”转换为“double *”

//void *pv = π//错误,无法从“const double *”转换为“void *”

const void *cpv = π//使用const void*保存const对象的地址

std::cout<<*((double*)cpv)<<std::endl;

    double *const cpd =&exp;//const指针

    *cpd = 2.7;//通过cosnt指针修改原对象的值

std::cout<<exp<<std::endl;

//cpd = &exp;//错误,不能给常量赋值

const double *const cptr_cd = π//指向const对象的const指针

    std::cout<<*cptr_cd<<std::endl;

 

return 0;

}



输出:

3.14

2.718

3.14

2.7

3.14

3.函数参数和const

1) const引用形参。

如果使用引用形参的唯一目的在于避免复制实参,即不修改相应的实参,则应该将其定义为const引用,否则const实参、字面值常量将无法调用此函数,因为非const引用形参只能与完全同类型的非const对象关联(若与const对象关联,那么通过非const引用形参去修改实参时会发生错误)。

例如:

#include <iostream>

#include <string>

using std::cout;

using std::endl;

using std::string;

//const引用形参举例

//非const引用形参只能与完全同类型的非const对象关联

std::size_t find_char(string &s,char c)

{

   string::size_type i = 0;

   while(i != s.size() && s[i] != c)

   ++i;

   if(i == s.size())

   return string::npos;

   else

   return i;

}

int main(int argc, char *argv[])

{   

//字面值常量为const对象,调用出错

if(find_char("Hello, world.",'.') != string::npos)

{

cout<<"a sentence."<<endl;

}

return 0;

}



提示错误:

 error C2664: “find_char”: 不能将参数 1 从“const char [14]”转换为“std::string &

应该将finc_char函数形参定义为const引用。

2) 函数返回const引用。

返回引用的函数返回左值,这样的函数可以用于任何要求使用左值的地方,这一点确实令人迷惑,需要注意,例如下面的代码可能违背你的原意:

#include <iostream>

#include <string>


using std::cout;

using std::endl;

using std::string;

//返回引用的函数返回左值需要注意

class CStu

{   

public:

CStu(){}

CStu(string sname,int iage)

:name(sname),age(iage){}

    string & getName(){return name;}//应声明返回const引用

private:

string name;

int age;

};

int main(int argc, char *argv[])

{

CStu stu("Tom",22);

cout<<"name: "<<stu.getName()<<endl;

    stu.getName() = "Jack";//修改了私有成员,违背原意

cout<<"name: "<<stu.getName()<<endl;

return 0;

}



使用getName函数返回的引用竟然修改了私有成员,这里违背了封装性设计原则。如果不希望引用返回值被修改,应该将其声明为const引用。这里需要将getName()函数返回值声明为const引用,需要修改其name时通过setName()修改。

3) 重载和const形参

仅当形参是引用或者指针时,形参是否为const才能实现函数的重载。可以基于指针指向const对象还是非const对象实现函数重载,编译器可以判断,如果实参是const对象,则调用带有const*类型形参的函数,否则如果实参不是const对象则调用带有普通指针形参的函数。注意,不能基于指针本身是否为const来实现函数的重载。

4.类的const成员函数

关于const成员函数的要点有以下要点:


Point 1: const成员函数的作用在于约定,这个成员函数不会去修改调用它的对象.const成员函数,不能修改非静态的成员变量,也不能调用任何非const成员函数. 如果一个函数声明为const而更改了成员,则编译器会报错.

例如下面的例子(来自:Constant Member Functions):

class Date
{
public:
   Date( int mn, int dy, int yr );
   int getMonth() const;     // 只读成员方法
   void setMonth( int mn );   // 写入函数,不能为const
private:
   int month;
};

int Date::getMonth() const
{
   return month;        // 不会修改任何内容
}
void Date::setMonth( int mn )
{
   month = mn;          // 修改成员变量
}
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
}

Point 2 : 在非静态成员函数中,隐含传递的this指针是一个常量指针,它保存调用这个函数的当前对象的地址.

对于一个类X来说,其非const成员中,隐含的this指针类型为'X* const',而const成员函数中,this类型为:'const X *const'.

所以在const成员函数中,无法通过this指针修改对象的成员变量和调用非const成员函数.


Point 3: const对象,指向const对象的指针或者引用只能用于调用其const成员函数,如果尝试用它们来调用非const成员函数则是错误的.C++ 允许基于成员函数是否为const进行重载.const对象调用const成员函数版本,而非const对象调用非const版本.

例如(来自: Function overloading and const keyword):

#include<iostream>
using namespace std;

class Test
{
protected:
    int x;
public:
    Test (int i):x(i) { }
    void fun() const
    {
        cout << "fun() const called " << endl;
    }
    void fun()
    {
        cout << "fun() called " << endl;
    }
};

int main()
{
    Test t1 (10);
    const Test t2 (20);
    t1.fun(); // call non-const version
    t2.fun(); // call const version
    return 0;
}

程序输出:

fun() called
fun() const called

5.const成员初始化

  初始化const类型的成员必须在构造函数初始化列表中进行初始化。例如如下代码:

//const成员初始化

class ConstInit {

public:

ConstInit(int i,int j)

{

   ival = i;

   cival = j;

   rival = ival;

}

private:

int ival;

    const int cival;

int &rival;

};

int main(int argc, char *argv[])

{

ConstInit ci;

}



将提示错误:

error C2758: “ConstInit::cival”: 必须在构造函数基/成员初始值设定项列表中初始化

        prog28.cpp(12) : 参见“ConstInit::cival”的声明

解决办法就是在初始化列表中初始化特殊类成员,如下所示代码:

//const成员初始化

#include <iostream>

using std::cout;

class ConstInit {

public:

ConstInit(int i=0):ival(i),cival(i),rival(i){}

private:

int ival;

    const int cival;

int &rival;

//只要初始化表达式是一个常量,可以再定义体中进行初始化

    static const int period = 30;

public:

static const unsigned int ARRAY[3];//静态常量数组

};

const unsigned int ConstInit::ARRAY[3] = {1,3,5};

int main(int argc, char *argv[])

{

ConstInit ci;

cout<<ConstInit::ARRAY[1];

}



 

你可能感兴趣的:(c++ const 限定符)