想小结一下const的种种用法和相关知识
1,const修饰数据
见文:const指针和引用
2,const修饰成员函数
表示该成员函数为常量对象调用,首先来看C++的对象和绑定的成员函数是如何实现的。
class A
{
public:
void Test(int _a)
{
a = _a;
}
private:
int a;
};
A::Test(int)是A的成员函数,一个函数是一段可执行代码,它和对象的数据是两回事,它只需要一份拷贝,相当于它们是静态的不变的(static),而非static数据成员是可以有多份拷贝,它们是动态的可变的,它们之间如何关联起来?通过this指针,在成员函数内对成员变量调用时会省略this指针,上面的语句相当于‘this->a = _a’而this指针是如何传进成员函数的?this指针如何传进去这不是程序员关心的,有的编译器通过函数第一个参数,比如上面的A::Test(int)实际上是A::Test(A* this,int);函数参数的压栈顺序是反的,从汇编的角度看,就是通过系统栈传递this指针,当然,有的还会通过寄存器,如何传进去的不用担心,结果是它已经传进去了。所以可以这样理解下面的C++代码:
A a;
a.Test(100);
理解为:
A a;
A::Test(&a,100);
好了,这样理解是最好的了,我们来看const成员函数,如果上面代码中a是常量,看看有什么问题:
const A a;
A::Test(&a,100);
由于Test()的第一个参数是A*,&a的结果是const A*,不能将一个指向常量的指针赋给一个一般指针,编译错误。所以简单在Test定义式中尾部加一个const:
void Test(int _a) const
{
a = _a;
}
就行了,它相当于第一个this参数的类型换成了const A*,即A::Test(const A* this,int);而一个A*指针是可以赋给const A*的。那这样的话,它和没有const修饰的成员函数可以重载了,对,这两种形式是可以重载。
好继续考虑,如果这样的话,const修饰的成员函数有什么不同?
this是一个指向常量的指针,所以this->将得到一些常量类型的成员,即在const修饰的成员函数内,是不能修改成员变量的。(外话,如果你硬要修改也是可以的,可以作const_cast转型,MSDN中有例子;或者由关键字mutable改变)
好了,我把由const修饰的成员函数的作用讲清楚了,两点:1,常量对象调用;2,不能修改成员变量。
设计的时候,是不是不管三七二十都上一个const和非const成员函数?对于一个接口,最小化后的接口集,从一个方面考虑,如果允许常量对象,你会把哪些接口对它开放,将开放的函数先改成const版,看能否全全工作,如果不能再重载一个非const版本。(1)如果你设计的类不允许有常量对象(思考,如何阻止不能产生常量?),所有接口都非const(2)允许有常量对象,对‘常量性操作’开放const成员函数。
3,const和static
const如果和static一起修饰会是什么后果?先看看static吧。它指‘静态的’,和‘常量的’是近义词,但是在程序中的实际意义相差蛮远。一份数据,首先有一个名字,但是同样一个名字,可能有多份数据,而且在不同时刻,数据所在存储区也是可变的,比如一般函数内的局部变量:
int foo(int x)
{
int a;
a = x*x;
return a;
}
这里的a就是可变的,它在运行期只是一个偏移量,从foo()调用的时候,系统栈的栈顶到变量a的偏移量。它随着调用的时刻不同,将在栈中不同的位置出现,甚至在一个递归的函数中,它可以同时在栈中出现多份数据。但是它只有一个名字a,它是‘动态的’。相对这种‘动态’,用static修饰的变量,将只有一份拷贝,即它的名字只指一份数据,在一个程序的一次运行中。但是这个唯一的静态数据,不一定是常量,它可以是变量。const约束一份数据不能被修改,static表明一份数据是独一无二的(static修饰成员函数时表示它们是和对象不相关的,没有this指针,修饰成员数据时表示这份数据只存在一个拷贝,不存在于不同的对象当中)。在类内部使用静态常量,可以像enum或者#define一样用,但是如果你非要取它们的地址,那就必须有它们的定义式,否则将引发连接错误,比如在一个类内部定义了静态常量,可以不在类外部做静态变量定义,而直接使用它们,就像真常量一样。这样说可能比较难理解,看例子吧:
class A
{
public:
static const int c = 100;
};void foo(int const& x)
{
}...
foo(A::c); // 引用传递需要取地址,连接错误
foo(0+A::c);// ok
...
相比于‘真常量’,比如enum和#define,它们只能是右值,即不能取地址,不能修改,甚至是运行期不存在的,而静态常量有同样的效果,如果你不给出它的定义式,如果取了地址就会有连接错误,不取地址就跟真常量一样,是编译期常量,可用于元编程中。一般const修饰只是约束了一般变量,这种约束只在编译期,为了让程序员不要犯错误,要善于利用const,让编译器查出尽可能多的错误;类内静态常量可以只有带初始化的申明式,利用它可以代替真常量。
评论