const 的用途:
1、什么是const?
常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。
2、为什么引入const?
const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。
1、 const用于修饰函数时,一般是const修饰类的成员函数(函数定义体),表示在函数体中成员变量不能改变;
其函数形式为:int ff(void) const;
class A{
public: intff(void) const{ x++; return x;} //错误
private:
int x;
}
但是这不是绝对的:
mutable class A {
public:
A(int i=0):test(i) { }
void SetValue(int i)const { test=i; }
private: mutable int test;//这里处理!
};
这样就可以改变了;
2、 const修改函数的参数值,表示参数的值不能被改变。
Void func(const inti){
i++; //出错
}
但这也不样绝对的
int f(const int x)
{
*const_cast<int*> (&x)+=5;
Cout <<x<<endl;
return x;
}
3 、可以节省空间,避免不必要的内存分配。 例如:
#define PI 3.14159 //常量宏
const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ......
double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
4、修饰常指针
const int *A; //const修饰指向的对象,A可变,A指向的对象不可变
int const *A; //const修饰指向的对象,A可变,A指向的对象不可变
int *const A; //const修饰指针A, A不可变,A指向的对象可变
const int *const A;//指针A和A指向的对象都不可变
5、在另一连接文件中引用const常量
extern const int i;//正确的引用
extern const int j=10;//错误!常量不可以被再次赋值 另外,还要注意,常量必须初始化! 例如: const inti=5;
6、为函数重载提供了一个参考。
class A { ......
void f(int i) {......} //一个函数
void f(int i) const {......} //上一个函数的重载 ......
};
constint* pi=new const int(10); 这里要注意2点:
1)const对象必须被初始化!所以(10)是不能够少的。
2)new返回的指针必须是const类型的。 那么我们可不可以动态创建一个数组呢? 答案是否定的,因为new内置类型的数组,不能被初始化。 这里我们忽视了数组是类类型的,同样对于类内部数组初始化我们也做出了这样的忽视。
关于Const函数的几点规则:
a. const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
b. const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.
c. const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.
e. 然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。
因为外部临时变量在使用之后就会消失了,所以加const就没有意义,而如果传的是地址,那么当函数结束后还是可以通过地址对原来函数里保存在堆上的变量进行操作。
const对象的成员不可修改,但const对象通过指针维护的对象却可以修改 const对象只能调用const成员函数是因为const函数不会改变成员对象,这点和const对象的本意是相同的,其他函数有可能会改变成员变量,所以编译器拒绝通过调用非const函数 Class foo{ Public: Void test1(){} Void test2() const{} } void test2()const { foo *temp = const_cast<foo*>(this); temp->test1(); } |
|
也就是说,转换成了一个非const的成员,C的转换权限太大,直接就把所有的东西都转换了..这里其实只转换了const属性,const_cast的功能在这里就是:把常量指针被转化成非常量的指针,并且仍然指向原来的对象。
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。
例如不要把函数int GetInt(void) 写成const int GetInt(void)。
同理不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。
例如:test(GetA());
如果返回值不是内部数据类型,将函数A GetA(void) 改写为const A & GetA(void)的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。
一般const修饰函数返回值是为了防止函数调用后作为左值用,如下面
get_val(vec, 0) = 3;//编译错误
对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)
准确的说const是修饰this指向的对象的
譬如,我们定义了
Class A{
Public: A(){m_y=10;}
void func1(int i){
m_y+=i;
}
Void func2(int i) const{
//func1(i); //错误
A* a =const_cast<A*>(this);
This->func1(i);
}
}
这里f函数其实有两个参数,第一个是A *const this, 另一个才是int类型的参数
如果我们不想f函数改变参数的值,可以把函数原型改为f(const int),但如果我们不允许f改变this指向的对象呢?因为this是隐含参数,const没法直接修饰它,就加在函数的后面了,表示this的类型是const A *const this。
const修饰*this是本质,至于说“表示该成员函数不会修改类的数据。否则会编译报错”之类的说法只是一个现象,根源就是因为*this 是const类型的
const char* const months[12]=
{
“January”,”February”…
}
第一个const防止字符串被修改,第二个确保数组中每个指针始终指向它最初指向的字符串。
外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)、无链接性(只能在当前函数或代码块中访问)
在C++(但不是在C语言中),const限定答对围住储存类型稍有影响。在默认情况下全局变量的链接性为外部的,但const全局变量的链接为内部的。也就是说,在C++看来,全局const宣言就像使用了static 说明符一样。
例如: const int fingers = 10;///same as static const int fingers = 10;
C++修改了常量类型的规则,让程序员更轻松,。例如,假设一组常量放在头文件中,并在同一个程序的多个文件中使用的该头文件。那么,预处理器将头文件的内容包含到每个源文件中后,所有的源文件都将包含类似 下面的这样的定义:
Const intfingers=10;
Const char *warning = “Wark”;
如果全局const声明的链接性像常规则变量那样是外部的,则根据单定义规则,这将出错。也就是说,只能有一个文件可以包含前面的声明,而其他文件必须使用extern关键字来提供引用声明。另外,只有未使用extern关键字的声明才能进行初始化:
externconst int finger ; // can’t be initialized
externconst char *warning;
因此,需要为某个文件使用一组定义,而其他文件使用另一组声明。然而,由于外部定义的const数据的链接 性为内部 的,因此可以在所有文件中使用相同的声明。
内部链接性还意味着,每个文件都有自己的一组常量,而不是所有文件共享一组常量。每个定义都是其所属文件私有的,这就是能够将常量定义放在头文件中的原因。这样,只要在两个源代码文件中包括同一个头文件,则它们将获得同一组常量。
如果出于某种原因,程序员希望某个常量 的链接性为外部的,则可以使用etern关键字来覆盖默认的内部链接性:
extern const intstates =50;
PS:函数也有此功能,内联函数除外,不用const修改。内联函数,这样,包含了头文件的每个文件都有内联函数的定义。然而,C++要求同一个函数的所有内联定义都必须相同。