C++编程思想笔记——const

const的最初动机是取代预处理器#define进行值替代,被用于指针、函数变量、返回类型、类对象及其成员函数。可以在编译器需要知道这个值的地方只用常量,同时执行常量折叠。

与使用#define 一样,使用const必须把const放进头文件里。C++中的const默认为内部连接,也就是说,const仅在const被定义过的文件里才是可见的,在链接时不能被其他编译单元看到。当定义一个常量时,必须初始化,除非用extern进行说明:

extern const bufsize;

虽然extern强制进行了存储空间分配(另外还有一些情况,如取一个const的地址,也要进行存储空间分配)【注释:extern申明后,】,但C++编译器通常并不为const分配存储空间,而是保存在符号表里(进程内存常量区?)。绝对部位任何const分配存储是不可能的,对于复杂结构,编译器建立存储(分配内存)会阻止常量折叠。


const可以用于集合,但编译器不能把一个集合存放在它的符号表(常量区)里,所以必须分配内存,这时在编译期间,编译器将不能获得集合元素的值,不能进行常量折叠。

cosnt int i[] = {1, 2, 3, 4} ;
float f[i[3]]; //非法
struct s {int i, j;};
cosnt s S[] = {{1, 2}, {3, 4}};
double d[s[1].j] //非法

const 在C和C++中的区别

常量的引进是在早期的C++版本中,当时标准C规范正在制定。
C中const的意思是"一个不能被改变的普通变量",在C中,它总是占用存储而且它的名字是全局符。C编译器不能把const看成一个编译器的常量。在C中如果写:
cosnt bufsize = 100;
char buf[bufsize];
因为bufsize占用存储的摸个地方,所以编译器不知道它在编译时的值,在C中可以这样书写
cosnt bufsize;
但在C++中是不对的,C编译器吧它当做一个声明,指明在别的地方有内存分配。因为C默认const是外部链接的,而C++默认为内部链接,所以同样的事在C++总,需要用rxtern把链接改成外部链接
extern const bufsize;
在C中使用限定符cosnt不是很有用,即使在常数表达式里(必须在编译期间被求出),想使用一个已命名的值,使用cosnt也不是很有用的。C迫使程序员在预处理器使用#define

指针

使用指针定义的技巧,是在表示符的开始处读它并从里向外读,const制定那个最靠近的
指向const的指针
cosnt int* x ;   //从表示符开始这样读:x是一个指针,它指向一个const int
int cosnt *x;    //x是一个指向恰好是const的int的普通指针


const指针
使指针本身本身成为一个const指针,必须把const表明的部分放在*的右边
int d = 1;
int* const x = &d;  //x是一个指针,这个指针是指向int的const指针

C++关于类型检查有器特别之处,可以把一个非const对象的地址赋个给一个const指针(因为也许又是不想改变某些可以改变的东西),然而不能将一个const对象的地址赋给一个非const的指针
int  d = 1;
const int e = 2;
int* u = &d;  //OK-- d not const
int* v = &e;  //illegal--e const
int* w = (int*)&e; //legal but bad practice

串字面值
限定词const是很严格的,cosnt没被强调的地方是有关串字面值
char*cp = “steven”;
编译器将接收它而不报错误,但从技术上讲,这是一个错误,因为串字面值(“steven”)是被编译器作为i个常量串建立的,所以引用串的结果是它在内存的首地址。所以串字面值实际上是常量串。然而编译器把他们作为非常量看待,这是因为有许多现在的C代码是这样做的。

函数参数和返回值

int f(const int i)
{
    int a = i * i;
    retutn a;
}

int f(int i)          //内部限定参数要优于参数限定,可以避免不清楚传值过程中隐藏的生成临时副本变量而引起的混淆
{
    const int& b = i;
    int a = b * b;
    return a;
}         
对于返回内部基本类型的函数来说,返回值const限定是没有意义的,但是如果返回的是自定义类型,则cosnt返回对象将不能做左值(不能被复制和修改)
临时变量
有时候,在求表达式值期间,编译器必须建立临时对象。想其他任何对象一样,他们需要存储空间而且必须被构造和删除。区别是我们从来看不到他们,编译器复制他们的去留以及他们存在的细节。临时变量自动地成为常量,因为我们通常接触不到临时对象,不能使用与之相关的信息,所以告诉临时变量做一些改变几乎肯定会出错

由于引用的语法(看起来像值传递)的原因,传递一个临时对象给带有一个引用的函数是可能的,第三不能传递一个临时对象给带有指针的函数--因为它必须清楚地带有地址。所以通过引用传递会产生一个在C中不会出现的新情况:一个总是常量的临时变量,它的地址可以被传递给一个函数。这就是为什么临时变量通过引用被传递给一个函数时,这个函数的参数一定是常量(const)引用。
class X {};
X f() { return X(); } //return by value
void g1(X&) {} //pass by non-const reference
void g2(const X&) {} //pass by const reference
void main()
{
    //Error: const temporary created by f():
    //g1(f());
    //OK: g2 takes a const reference:
    g2(f());
}

 类

程序员可能想在一个类里建立一个局部常量,将他用在常数表达式里,这个常数表达式在编译期间被求值。然而,const的意思在类里是不同,必须使用另一技术--枚举

要保持类对象为常量通常比较复杂。编译器能保证一个内部数据类型为常量,但不能控制一个类中错综复杂的事物。为了保证一个类对象为常量,引进了const成员函数:对于一个常量对象,只能调用const成员函数。

在一个类里,const恢复它在C中的一部分意思。它在每个类对象里分配存储并代表一个值,这个值一旦被初始化以后就不能改变。在一个类里使用const的意思是“在这个对象寿命期内,这是一个常量”。然而, 对这个常量来讲,每个不同的对象可以包含一个不同的值。类里的const成员变量必须放在构造函数的初始化列表中初始化

因为在类对象里进行了内存分配,编译器不能知道const的内容是什么,所以不能把它用作编译期间的常量。不能这样写:

class bob {
    const size = 100; //illegal
    int array[size];  //illegal
    //...
普通的八法是使用不带实例的无标记的enum,枚举的所有值必须在编译是建立,它对类来说是局部的,但常数表达式能得到它的值

class  bunch
{
    enum { size = 1000 };
    int i[size];
}
使用enum不会占用对象中的存储空间,枚举常量在编译时被全部求值

cosnt对象必须保证对象的数据成员在对象寿命周期内不被改变。可以很容易保证共有数据不被改变,但不能知道哪些成员函数会改变数据。如果一个成员函数声明为const函数,在一个const对象就可以调用改成员函数。没有被声明为const的函数,被认为会改变对象成员变量,因此不能被const对象调用。

构造函数和析构函数不能是const函数

按位与按成员cosnt

按位const的意思是对象中的每个位是固定的,所以对象的每个位映像从不改变。

按成员const的意思是虽然整个对象从概念上讲是不变的,但是某成员可能有变化。但编译器被告知一个对象是cosnt对象时,它将保护这个对象。有两种方法可以在const成员函数里改变数据成员:

1.强制转换const (过时,不推荐) 

class  Y
{
    int i, j;
public:
    Y() { i = j = 0; }
    void f() const;
};
void Y::f() const 
{
    //i++; Error--const member function
    ((Y*)this)->j++;  //OK cast away const-ness
}
void main()   
{
    const Y yy;
    yy.f();   //actually changes it!
}

2.mutable

 
class  Y
{
    int i;
    mutable int j;
public:
    Y() { i = j = 0; }
    void f() const;
};
void Y::f() const 
{
    //i++; Error--const member function
    j++;  //OK mutable
}
void main()   
{
    const Y yy;
    yy.f();   //actually changes it!
}

volatile

防止编译器优化,每次读数据操作都务必执行

可以建立const volatile对象,这个对象不能被程序员改变,但看通过外面的工具改变
将像cosnt一样,可以对数据成员、成员函数和对象本身使用volatile,并且也只能为volatile对象调用volatile成员函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值