const

const 之 指针 篇

常量指针 / 常指针

常量指针本质上是指针,表示指向常量的指针(变量)。
一般“常指针”也指常量指针。

声明方式如下: const T *A; or T const *A;
const修饰指向的对象,指针A可变,A指向的对象不可变。
所谓指向的对象不可变,指的是不能通过该指针去修改指向对象的内容,性质上等同于一个只读指针。但是可以通过所指向对象本身去修改内容。

比如:

int a;
iut c;
cosnt int *b = &a;
a = -1;   //正确,通过变量a本身去修改内容是合法的
*b = -1;  //错误,通过指针b去修改指向对象的内容是不合法的
b = &c;	//正确,b是指针,可以修改本身的值

另外,由以上例子也可以看出,常量指针(指向常量的指针)也可以指向变量。

指针常量

指针常量本质上是常量,即一个指针类型的常量。声明的时候必须初始化。

声明方式: T *const A;
const修饰指针A,A不可变,A指向的对象可变。

比如:

int a = 1, int c;
int *const b = &a;
*b = -1;   //正确,指针b指向的对象可变,内容可修改
b = &c;   //错误,本质上是常量,不可以修改指向的对象

常指针常量

声明方式如下:
const T *const A;
显而易见,指针A和A所指向的对象都不可改变。


const 之 函数 篇

const 修饰 函数参数

const引用,通常应用于定义变量的只读属性的别名,作为函数传入形参,避免在调用函数中意外地被改变。const引用是只读的,常量不能作为左值。

const修饰对象变量(非常量)传入形参( 输出形参不能用const修饰),因为不会产生临时变量,节省了临时对象构造,复制,析构的过程,可提高程序效率。但是对于内部数据类型,没有构造,析构的消耗,复制过程也非常快,因此“值传递”与“引用传递”的效率几乎一样。

const的引用及初始化

对一个常量进行引用,编译器会首先建立一个临时变量,然后将该变量的值置入临时变量中,对该引用的操作就是对该临时变量的操作,对常量的引用可以用其他任何引用来初始化,但是不能改变。

当初始化值是一个左值(可以取得地址)时,没有任何问题;而当初始化值不是一个左值时,则只能对一个常引用赋值,而且赋值的过程是,首先将值隐式转换到类型T,然后将这个转换结果存放在一个临时对象里,最后用这个临时对象初始化该引用变量。

比如:

 1). double &dr = 1;            //错误,初始化值不是左值
 2). const double &cdr = 1;     //正确

其中,第二种方法对应的执行过程如下:

double temp = double(1);
const double &cdr = temp;

如果将第一种方法改为合法操作,对应如下:

const int ival = 1024;
const int &refval = ival;

在使用const引用进行函数调用时,即作为函数传入形参,需注意:

#include<iostream>
#include<string>
using namespace std;

void bar(string &s){
	cout<<s<<endl;
}

int main(){
	bar("hello world");              //非法操作
	/* string s = "hello world";
	bar(s);*/                       //传入的是变量,合法
	return 0;
}

原因:

“hello world”串会产生一个临时变量,而在c++中,临时对象都是const类型的,因此该表达式试图将一个const类型的对象转换为非const类型,这是非法的。
应该改为将函数声明改为bar(const string &s);
或者采用值传递bar(stirng s);

const 修饰 函数返回值

const修饰函数返回值,表示返回值(即指针或引用)的内容不能修改。

之所以一定是指针或引用,是因为函数返回值采用“值传递”的方式,则返回值存储于外部临时变量,const修饰没有任何意义。

一般采用引用返回的方式比较少见,常见于类的赋值函数,目的是为了实现链式表达,即A = B = C;

普通情况下,(A = B) = C;的表达不常见,但是合法。但是const修饰的函数返回值该操作会报错,因为const常量只能有一次初始化,不能作为左值进行赋值操作。


const 之 类 篇

const 修饰 数据成员

结合关键字static,对数据成员的初始化进行分析,主要有以下几种:

非静态数据成员(const T)
class example{
	private:
		const int count;
	public:
		example() = default;
		example(const int &count):count(count){};         //只能初始化列表初始化
};

非静态常量数据成员不能在类内初始化,即不能在声明时初始化,也不能在构造函数中初始化,只能在构造函数的初始化列表中初始化。因此,非静态数据成员必须要有构造函数
1)因为const常量数据成员只能有一次初始化的过程,若在构造函数内部或是在类外,都是对已经完成构造的对象数据成员进行赋值而不是初始化。
2)不同的对象const数据成员的数据可以不同,对象没被创建时,编译器不知道值是什么。

静态非常量数据成员(static T)
class example{
	private:
		static int count;
};
int example::count = 0;        //只能类外初始化,不用再加static关键字

静态非常量数据成员不能在类内初始化,且只能在类外初始化,初始化时不必再加static关键字。
因为static数据成员是所有类对象共享的,在全局数据空间中仅存在一份实例,若是在类内初始化,则每个对象都会包含一个该数据成员,这是矛盾的。

静态常量数据成员(static const T)
class example{
	private:
		static const int count = 0;
};
//const int count = 0;

静态常量数据成员既可以在类内初始化,也可以在类外初始化

const 修饰 成员函数

const修饰成员函数由两种形式:

  1. 当const位于函数前,即const T *Funtional(){};表示修饰函数返回值(见函数篇);
  2. 当const位于函数后,即T Funtional() const{};表示修饰该函数的this指针;

每个const成员函数都有隐含的指针参数(class)A * const this;const修饰指针this,表示该成员函数不会修改类的数据,否则编译报错。
const函数可以访问普通成员变量,但是不能修改。除非该数据成员声明有mutable关键字。


const 与 #define(宏定义)

编译器处理

#define在预处理阶段展开(编译阶段替换),无法进行调试;
const常量于编译阶段运行时使用,可用某些集成化的工具调试。

类型

#define是简单的类型替换,没有数据类型,因此替换也没有类型安全检查,可能产生边际效应,出现错误;
const常量则有具体的数据类型,在编译阶段进行类型安全检查。

存储与内存分配

#define在预处理阶段展开,不会分配内存(预处理展开,编译替换才分配内存)
const常量编译阶段使用,需要分配内存。
宏定义存储于程序的代码段,而const常量存储于程序的data段。

#define PI 3.1415			//宏定义常量
const double pi = 3.1415;	//const定义,并未将Pi置入只读存储器
double I = PI;				//为PI分配内存,仅此一次,以后不再分配
doubel i = pi;				//编译替换,每次替换均分配内存

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


const 之 实现机制

const 之 实现机制(借鉴,原理待求证)

const只是编译阶段的保护,编译期间会检查const变量有没有被修改,如果有代码尝试修改,编译报错。const一定程度上在编译阶段使得该变量成为一个常量,但是并没有实现保证该变量在运行阶段内存中的值不被修改。
const关键字是用于约束程序员和编译器为的,而不是程序员用以保证程序正确性的良药,除了全局常量外,没有任何机制保证const声明的常量的不可修改性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值