const double PI = 3.14159;
const int i, j = 0; // error: i is uninitialized const
// file_1.c extern const int bufSize = fcn(); // file_2.c extern const int bufSize; for (int index = 0; index != bufSize; index++) //...
const和指针有两种交互类型:指向const对象的指针和const指针。
const double *cptr; // cptr may point to a double that is const在上面的语句中,const限定的是cptr指针所指向的对象,而不是cptr本身,也就是说,cptr本身并不是const, 所以不需要对cptr进行初始化。如果需要的话,允许给cptr重新赋值,但是不能通过cptr修改其所指对象的值:
*cptr = 42; //error: *cptr might be const
const double pi = 3.14; double * ptr = π // error: ptr is a plain pointer const double * cptr = π // ok: cptr is a pointer to const
const double pi = 3.14; const void *cpv = π // ok void *pv = π //error
double pi = 3.14; const double * cptr = π // ok, but can't change pi through cptr
double pi = 3.14; double pi2 = 6.28; double * const conptr = π conptr = &pi2; // error: conptr is const
const double pi = 3.14; // pi_ptr is const and points to a const object const double * const pi_ptr = π可以从右向左阅读上述声明语句:”pi_ptr首先是一个const指针,指向double类型的const对象“。
在说明const引用之前,先复习一下引用的一些特性:
int a = 1024; int &refa1 = a; // ok int &refa2; // error int &refa3 = 2048; // error: mismatch type
const引用是指向const对象的引用。之所以不是“引用是const”,是因为引用本身就被默认为const,当一个引用被定义时,必须被初始化,绑定到一个对象上,并且不可以改变。
const int ival = 1024; const int &refVal = ival; //ok int &ref2 = ival; //error: nonconst reference to a const object.
int z = 0; const int &refZ = z; refZ = 1; //error.
int i = 42; const int &refI = 42; // ok const int &refI2 = i + 42; //ok
double dval = 3.14; const int &ri = dval;编译器会把这些代码转换成如下形式的代码:
double dval = 3.14; int temp = dval; // create temporary int from the double const int &ri = temp; //bind ri to that temporary
void function(const char* Var); // 参数指针所指内容为常量,不可改变
void function(const string & s1); // s1是一用,所以不复制形参,又因为形参是const引用,所以该函数不能修改是s1引用对象的内容。优点:避免复制形参,提高效率,同时防止修改实参。
同时更大的优点是,const引用类型的形参更灵活:
如上述定义的函数传递的形参可以是一个一个字符串常量,如:
function("hello world");因为const引用形参可以与不同类型的对象相关联。
但是如果将函数声明为如下的形式:
void function(string & 2);
由于非const引用形参只能与完全同类型的非const对象关联,所以再传递字符串常量就会发生编译错误。
class A void function(const char* Var); { void function_1(); void function() const; }
在解释这个const所起的作用之前,回忆一下,每个成员函数都有的一个额外的隐含的形参this。
考虑下面的调用语句:
a.function_1();
A::function_1(&a);即隐含的this初始化为调用函数的对象的地址。
那么跟在函数声明的形参表后面的const所起的作用就是:改变this形参的类型。
在调用a.function()时,隐含的this形参将是一个指向a对象的const A*类型的指针。
const成员函数的特性:
初始化const成员变量必须在执行构造函数的函数体之前完成,所以,唯一的机会就是在构造函数初始化列表中初始化const成员变量。
Class A { ... private: const int nValue; public: ...... A(int x): nValue(x) { ... }; }
下面的构造函数是错误的:
class ConstRef { public: ConstRef(int ii); private: int i; const int ci; int &ri; }; ConstRef::ConstRef(int ii) { i = ii; ci = ii; // error: cannot assign to a const ri = i; }
ConstRef::ConstRef(int ii): i(ii), ci(i), ri(ii) {}没错,引用类型数据成员和const成员变量一样,初始化的唯一机会是在构造函数初始化列表中。
先回忆一下类的static成员的基本性质:
通常,static数据成员不像普通数据成员,static成员不是用过类构造函数进行初始化,由于static成员只能被初始化一次,所以应该在定义时初始化,且在类的定义体外部定义。
#include<iostream> using namespace std; class Tester { public: static int a; }; int Tester::a = 10; int main() { cout << Tester::a << endl; return 0; }不需要实例化这个类,即可访问static成员。
这个规则的一个例外就是:只要初始化式是一个常量表达式,整型const static 数据成员就可以在类的定义体中进行初始化,需要注意的是,该数据成员仍必须在类的定义体之外进行定义,而不必再指定初始值。
#include<iostream> using namespace std; class Tester { public: const static int b = 0; }; const int Tester::b; int main() { cout << Tester::b << endl; return 0; }
这个程序在电脑上编译通过,但是遭到管家报警自动删除,警报为后门程序。。
参考资料:
《C++Primer(4th)》
《C++高级编程(2th)》