C++中const 关键字的用法

const修饰变量

const 主要用于把一个对象转换成一个常量,例如:

  1. const int size = ;
  2. size = ; // error: assignment of read-only variable

上面的例子中,定义size为常量并初始化为512,变量size仍是一个左值,但是现在这个左值是不可修改的,任何修改size的尝试都会导致编译错误。

因为常量在定以后就不能被修改,因此const对象定义时必须初始化,否则会引起编译错误,例如:

  1. const int size; // error: uninitialized const

在全局作用域里定义非const变量时,它在整个程序中都可以访问。我们可以把一个非const变量定义在一个文件中,假设已经做了合适的声明,就可以在另外的文件中使用这个变量:

  1. // file1.cpp
  2. int counter; // definition
  3.  
  4. // file2.cpp
  5. extern int counter; // use counter from file1
  6. ++counter; // increments counter defined in file1

在全局作用域里声明的const变量是定义该对象的文件的局部变量,此变量只存在于那个文件中,不能被其它文件访问。

通常头文件中不能存放变量定义,但const变量是一个例外,当我们在头文件中定义了const变量后,每个包含该头文件的源文件都有了自己的const变量,其名称和值都一样。

通过指定const变量为extern,就可以在整个程序中访问const对象,例如:

  1. // file1.cpp
  2. extern const int counter = ; // definition
  3.  
  4. // file2.cpp
  5. extern const int counter; // use counter from file1

总结:

1、非const变量默认为extern。

2、要使const变量能够在其它的文件中访问,必须显式地指定它为extern。


const修饰引用

引用(reference)就是对象的另一个名字,在实际应用中,引用主要用作函数的形参。

引用必须用与该引用同类型的对象初始化:

  1. int i = ;
  2. int &r1 = i; // ok
  3. int &r2; // error: a reference must be initialized
  4. int &r3 = ; // error: initialize must be an object

当引用初始化后,不能将其再绑定到另一个对象上:

  1. int i = ;
  2. int j = ;
  3. int &r1 = i; // ok
  4. r1 = j; // i=37

const引用是指向const对象的引用,

  1. const int i = ;
  2. const int &r1 = i; // ok
  3. int &r2 = i; // error: nonconst reference to a const object

上面的例子中,可以读取但不能修改r1,因此,任何对r1的赋值都是不合法的。

同理,用i初始化r2也是不合法的,r2是普通的非const引用,不能指向const对象。

const 引用可以初始化为不同类型的对象或者初始化为右值,如字面值常量:

  1. int i = ;
  2. const int &r1 = i;
  3. const int &r2 = ;
  4. const int &r3 = r2 + i; //

const引用绑定到不同但相关类型的对象上是合法的,如下:

  1. double i = 42.1;
  2. const int &r1 = i; // ok, r1=42
  3. int &r2 = i; // error

这是因为const引用是只读的,编译器会自动创建一个临时变量,然后将const引用绑定到这个临时变量上:

  1. int temp = i;
  2. const int &r1 = temp;

总结:

1、普通非const引用只能绑定到与该引用同类型的对象;

2、const引用则可以绑定到不同但相关的类型的对象,或绑定到右值;


const修饰指针

1、const指针

前面提到,const对象在定义的同时必须初始化,const指针也遵循这一规则。

作为常量,const指针的值不能被修改,这就意味着const指针初始化以后不能再指向其它对象,任何试图给const指针赋值的行为都会导致编译错误。

  1. int i = ;
  2. int j = ;
  3. int* const p1 = &i; // ok
  4. int* const p2; // error, uninitialzed const
  5. p1 = &j; // error, assignment of read-only variable

const指针本身虽不能修改,但却可以通过它修改所指向的对象的值,

  1. int i = ;
  2. int* const p1 = &i; // ok
  3. *p1 = ; // i=37

2、指向const对象的指针

如果指针指向const对象,则不允许用指针来改变其所指的const值,为了保证这个特性,C++强制要求指向const对象的指针必须具有const特性:

  1. const int i = ;
  2. int* p1 = &i; // error
  3. int* const p2 = &i; // error
  4. const int* p3 = &i; // ok

指向const对象的指针本身并不是const的,因此定义时可以不必进行初始化,且可以再指向其它对象;

允许把非const对象的地址赋给指向const对象的指针。

  1. const int i = ;
  2. int j = ;
  3. const int* p1; // ok
  4. p1 = &i; // ok
  5. p1 = &j; // ok
  6. *p1 = ; // error

但不允许指向const对象的指针修改其所指向的值,如上例,p1指向非const变量j,但仍不允许通过p1修改变量j。

3、指向const对象的const指针

  1. int j = ;
  2. const int* const p1 = &j;

这里,既不能修改p1所指向的对象的值,也不允许修改该指针的指向(即p1中存放的地址值)。

4、const和typedef

  1. typedef int *pint;
  2. pint const pt; // 该语句相当于int *const pt,
  3. const pint pt;   //该语句还是相当于int *const pt,而非const int *pt

如上面的例子,把const放在类型pint之前,容易引起对所定义的真正类的误解,要特别注意。

总结:

1、const指针,const修饰的是指针;

2、指向const对象的指针,const修饰的是指针所指向对象的类型;


const修饰迭代器

标准库为每一种标准容器(如vector)定义了一种迭代器类型,迭代器是一种检查容器内元素并遍历元素的数据类型。

例如:

  1. vector<int>::iterator iter = vec.begin();
  2.  
  3. for (; iter!=vec.end(); ++iter) {
  4. cout<< *iter << endl;
  5. }

1、const迭代器

声明一个const迭代器时,必须初始化迭代器,一旦被初始化后,就不能改变它的值,但可以改变它所指向的元素的值:

  1. vector<int> vec();
  2. const vector<int>::iterator iter1 = vec.begin();
  3. *iter1 = ; // ok
  4. iter1++; // error

2、指向const的迭代器(const_iterator)

每种容器类型还定义了一种名为const_iterator的类型,该类型只能用于读取容器内元素,但不能改变其值。

对const_iterator类型解引用时,返回的是一个const值,不允许用const_iterator进行赋值,例如:

  1. const vector<int>::iterator iter1 = vec.begin();
  2. vector<int>::const_iterator iter2 = vec.begin();
  3.  
  4. *iter1 += ; // ok
  5. *iter2 += ; // error

对const_iterator迭代器,它自身的值可以改变(即指向不同的元素),但不能用来改变其所指向的元素的值。

const_iterator迭代器可以用来指向一个const的vector对象,而const迭代器对象不被允许,因为它可能改变所指向元素的值,而这个元素是只读的。

  1. const vector<int> vec();
  2. const vector<int>::iterator iter1 = vec.begin(); // error
  3. vector<int>::const_iterator iter2 = vec.begin(); // ok

const修饰类

1、const数据成员

构造函数分为两个阶段执行:a) 初始化阶段,在初始化列表中完成;b) 普通的计算阶段,在构造函数函数体中完成。

在构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化:对于类类型数据成员,运行其默认构造函数完成初始化;内置或者复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0.

没有默认构造函数的类类型成员,以及const或引用类型的成员,必须在构造函数初始化列表中进行初始化。

  1. class T {
  2. public:
  3. T(int k);
  4.  
  5. private:
  6. int i;
  7. const int ci;
  8. int &ri;
  9. };
  10.  
  11. // no explicit constructor initializer
  12. T::T(int k)
  13. {
  14. i = k; // ok
  15. ci = k; // error: cannot assign to a const
  16. ri = i; // error: unitialized
  17. }
  18.  
  19. // ok, explicit initialze reference and const members
  20. T::T(int k):i(k),ci(k),ri(i) {}

2、const函数成员

在类定义中,既可以定义const数据成员,也可以定义const函数成员。

在定义const成员函数时:

1、const关键字必须同时出现在声明和定义中,若只出现在一处,会出现编译错误;

2、const成员函数不能改变其所操作对象的数据成员(mutable成员除外);

3、构造函数不能为const,创建类类型的const对象时,运行一个普通构造函数来初始化该const对象即可。

例如:

  1. class Sale_item {
  2. public:
  3. double avg_price() const;
  4. bool same_isbn(const Sale_item &rhs) const {return isbn == rhs.isbn;}
  5.  
  6. private:
  7. string isbn;
  8. unsigned units_sold;
  9. double revenue;
  10. };
  11.  
  12. double Sale_item::avg_price() const
  13. {
  14. if (units_sold)
  15. return revenue / units_sold;
  16. else
  17. return ;
  18. }

我们知道,对于非static成员函数,都有一个隐含的this参数,this指针与调用成员函数的对象绑定在一起:

1、对于非const成员函数,this是一个指向类类型的const指针,可以改变this所指向的值,但不能改变this所保存的地址;

2、对于const成员函数,this是一个指向const类类型的const指针,既不能改变this所指向的值,也不能改变this所保存的地址;

3、不能从const成员函数返回指向类对象的普通引用,const成员函数只能返回*this作为一个const引用。

基于成员函数是否为const,可以重载一个成员函数;同样地,基于一个指针形参是否指向const,可以重载一个成员函数。

const对象只能使用const成员,非const对象可以使用任一成员,但非const版本是一个更好的匹配。

  1. class Screen {
  2. public:
  3. Screen():contents("hello\n"){}
  4.  
  5. Screen& display(ostream &os)
  6. { os<<"non const:\t"<<contents; return *this;}
  7.  
  8. const Screen& display(ostream &os) const
  9. { os<<"const:\t"<<contents; return *this;}
  10.  
  11. private:
  12. string contents;
  13. };
  14.  
  15. int main()
  16. {
  17. Screen s1;
  18. const Screen s2;
  19.  
  20. s1.display(cout); // non const version
  21. s2.display(cout); // const version
  22. }

3、可变数据成员(mutable)

可变数据成员永远都不能为const,即使它是const对象的成员时也如此。因此,const成员函数可以改变mutable成员。

  1. class T {
  2. public:
  3. T():len(){}
  4.  
  5. void increment() const
  6. { len++; cout<<len<<endl;}
  7.  
  8. private:
  9. mutable int len;
  10. };
  11.  
  12. int main()
  13. {
  14. const T t;
  15. t.increment(); // len = 1
  16. t.increment(); // len = 2
  17. }

如上例,const对象调用const成员函数,改变了mutable数据成员。

4、static类成员

static成员是类的组成部分,但不是任何对象的组成部分;

static成员函数没有this指针,static成员函数不能声明为const,也不能声明为virtual;

static数据成员必须在类定义体的外部定义,static成员不是通过构造函数进行初始化,而是应该在定义时进行初始化。

const static整型数据成员在类的定义体中初始化时,该数据成员仍须在类的定义体之外进行定义。

C++中const 的各种用法的更多相关文章

  1. C/C++ 中 const 修饰符用法总结

    C/C++ 中 const 修饰符用法总结 在这篇文章中,我总结了一些C/C++语言中的 const 修饰符的常见用法,供大家参考. const 的用法,也是技术性面试中常见的基础问题,希望能够帮大家 ...

  2. C++中const简介及用法

    1.const简介 C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,本人根据各方面查到的资料进行总结如下,期望对朋友们有所帮助. Const 是C++中常用的类型修饰 ...

  3. C/C++中const关键字的用法及其与宏定义的比较

    1.const关键字的性质 简单来说:const关键字修饰的变量具有常属性. 即它所修饰的变量不能被修改. 2.修饰局部变量 ; ; 这两种写法是等价的,都是表示变量的值不能被改变,需要注意的是,用c ...

  4. C/C++中const关键字的用法及其与宏常量的比较

    1.const关键字的性质 简单来说:const关键字修饰的变量具有常属性. 即它所修饰的变量不能被修改. 2.修饰局部变量 ; ; 这两种写法是等价的,都是表示变量的值不能被改变,需要注意的是,用c ...

  5. [原创] 基础中的基础(二):C/C++ 中 const 修饰符用法总结

    在这篇文章中,我总结了一些C/C++语言中的 const 修饰符的常见用法,供大家参考. const 的用法,也是技术性面试中常见的基础问题,希望能够帮大家梳理一下知识,给大家一点点帮助.作者是菜鸟一 ...

  6. 转载----C/C++ 中 const 修饰符用法总结

    感谢原创作者,写的好详细.不忍错过,所以转载过来了... 原文地址: https://www.cnblogs.com/icemoon1987/p/3320326.html 在这篇文章中,我总结了一些C ...

  7. 关于c++中const的基本用法

    c++中的const 有点类似于c里的宏定义#define,但是似乎是在宏定义基础上的代码优化,具体我解释不清,下面主要提到的是 const 在c++中的3中基本用法: 1.指向常量的指针 例如:co ...

  8. C/C++中const的用法 分类: C/C++ 2015-07-05 00:43 85人阅读 评论(0) 收藏

    const是C语言的关键字,经C++进行扩充,变得功能强大,用法复杂.const用于定义一个常变量(只读变量),当const与指针,引用,函数等结合起来使用时,情况会变得复杂的多.下面将从五个方面总结 ...

  9. C++中const用法详解

    本文主要内容来自CSDN论坛: http://bbs.csdn.net/topics/310007610 我做了下面几点补充. 补充: 1. 用const声明全局变量时, 该变量仅在本文件内可见, 类 ...

随机推荐

  1. poj3177 && poj3352 边双连通分量缩点

    Redundant Paths Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12676   Accepted: 5368 ...

  2. SpringMVC学习系列(8) 之 国际化

    一.基于浏览器请求的国际化实现: 1)在 spring的配置文件中添加 <bean id="messageSource" class="org.springfram ...

  3. navigate连接MySQL报错:navigate your password has expired to log in your must change it using a client that supports

    如图: 终端进入mysql: 第一次show databases的的时候,密码过期了,然后重置密码为12345,再次就可以显示了 参考连接:http://www.jb51.net/article/79 ...

  4. 用 unoconv 将 xls 转换成 csv

    在 Linux 下,用 unoconv 将 xls 转换成 csv. unoconv -f csv -v input.xlsx

  5. Python 学习笔记9(装饰器,decorator)

    31 装饰器 装饰器可以对一个函数.方法或者类进行加工,是一种高级的python语法. 装饰函数 接收一个可调用对象作为输入参数,并返回一个新的可调用对象. 把函数传递给装饰器,然后增加新的功能,返回 ...

  6. 【BZOJ-2229】最小割 最小割树(最大流+分治)

    2229: [Zjoi2011]最小割 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1565  Solved: 560[Submit][Status ...

  7. 【poj1177】 Picture

    http://poj.org/problem?id=1177 (题目链接) 题意 求矩形周长并. Solution 转自:http://www.cnblogs.com/Booble/archive/2 ...

  8. NOIP2013

    DAY1 转圈游戏 列出式子(x+km)%n,快速幂. // codevs3285 #include<algorithm> #include<iostream> #includ ...

  9. 深入了解Mvc路由系统

    请求一个MVC页面的处理过程 1.浏览器发送一个Home/Index 的链接请求到iis.iis发现时一个asp.net处理程序.则调用asp.net_isapi 扩展程序发送asp.net框架 2. ...

  10. cmd执行sql文件

    string infile = @"C:\Users\yudm\Desktop\test\Patch.sql"; Process sqlprocess = new Process( ...