一、const 用法总结
1、可以对const 的用法做个小总结:
const int n = 100; //定义常量
const Test t(10);
const int & ref = n; //const引用
int& ref = n; //Error
【const与指针】
const int* p; //const出现在*前面,表示*p是常量 (*p = 200; //Error)
int * const p2; //const出现在*后面,表示p2是常量 (p2 = &n2; //Error)
const int* const p3 = &n; //*p3是常量,p3也是常量
【类】
如果有const成员,const成员的初始化必须只能在构造函数初始化列表中进行
const修饰成员函数,表示该成员函数不能修改对象状态,也就时说它只能访问数据成员,但是不能修改数据成员
【例子】
#include <iostream> using namespace std; int main() { const int n = 100; int n2 = 200; int n3 = 300; const int* p = &n; *p = 200; int* const p2 = &n3; p2 = &n2; const int* const p3 = &n; return 0; }
运行结果:
test.cpp:11:7: 错误: 向只读位置‘* p’赋值
test.cpp:13:8: 错误: 向只读变量‘p2’赋值
2、const一些常见的问题
(1)由于const常量在定义后就不能修改,所以定义时必须初始化。
const int i, j = 0; //错误,i必须初始化
(2)全局const变量与非const变量
——在全局作用域定义非const变量时,在整个程序中都可以访问。
//file1.cpp int counter = 10;
//file2.cpp #include <iostream> using namespace std; int main() { extern int counter; for(int i = 0; i != counter; i++) { cout << i << " "; } cout << endl; }
运行结果:
0 1 2 3 4 5 6 7 8 9
——在全局作用域定义const变量时是定义该对象的文件的局部变量,不能在整个程序中可以访问。需要在定义的时候在const前加上extern关键字,才可以在整个程序中访问。
//file1.cpp extern const int counter = 10; //const int counter = 10; //这样定义在file2中不能访问到
//file2.cpp #include <iostream> using namespace std; int main() { extern const int counter; for(int i = 0; i != counter; i++) { cout << i << " "; } cout << endl; }
运行结果:
0 1 2 3 4 5 6 7 8 9
(3)在C++中主要是用const替代#define的功能,建议在C++中,用const取代#define。优点有很多,比较突出的优点是编译器会对const进行类型安全检查,而#define没有类型安全检查,在字符替换时可能会产生意料不到的错误。
(4)指针在不同地方的含义
——double* ptr = &value;
答:ptr是一个指向double类型的指针,ptr的值可以改变,可以通过ptr改变value的值。
——const double* ptr = &value;
答:ptr是一个指向const double类型的指针,ptr的值可以改变,不能通过ptr改变value的值。
——double* const ptr = &value;
答:ptr是一个指向double类型的const指针,ptr的值不能改变,可以通过ptr改变value的值。
——const double* const ptr = &value;
答:ptr是一个指向const double类型的const指针,ptr的值不能改变,不能通过ptr改变value的值。
【一个有趣的例子】
看下面代码:
#include <iostream> int main() { char* const a[2] = {"abc", "def"}; a[0] = "ABC"; //错误 *(a+1); //正确 }
解释:a[2]是一个数组,可以当作是指针*a,然后根据上述的理解,a的值可以改变,不能通过a改变数组的值。
(5)作用区的const应用
【例子1】
下面的变量p和它所指向的字符串分别存放在()
#include <iostream> int main() { char* p = "hello world"; }
A、堆和常量区 B、栈和栈 C、栈和常量区 D、栈和堆
解答:C。"hello world"存放在文字常量区,变量p存放在栈上。
——现在做些修改
#include <iostream> int main() { char* p = "hello world"; p[0] = 'a'; }
运行结果:
导致运行时崩溃,因为程序对文字常量区的内容试图做修改,而这是不允许的。此时可以改一下p的定义:const char* p = "hello world"; 这样编译器就会直接识别出错误。
(6)const修饰返回值
【一个例子】
猜猜下面程序的运行结果:
#include <iostream> using namespace std; char* getstr() { char p[] = "hello world"; return p; } int main() { char *str = NULL; str = getstr(); cout << str << endl; return 0; }
解释:结果是乱码。p是一个数组,其内存分配在栈上,故getstr()返回的是指向“栈内存”的指针,该指针的地址不是NULL,但其原来的内容已经被清楚,新内容不可知。
——解决方案:由于栈容量小,每次作用域结束就会清除;因此可以不存在栈区,存在静态区(全局区)或者是堆区。比如下述,加上static,存到静态数据区。
#include <iostream> using namespace std; char* getstr() { static char p[] = "hello world"; return p; } int main() { char *str = NULL; str = getstr(); cout << str << endl; return 0; }
运行结果:
hello world
(7)const用来修饰函数参数
——【例子1】
#include <iostream> void fun(int* i){ } int main() { const int a = 1; fun(&a); return 0; }
运行结果:编译出错
解释:在函数中加const则可以运行,void fun(const int* i){ }
——【例子2】
#include <iostream> void fun(int& i){ } int main() { fun(1); return 0; }
运行结果:编译出错
解释:在函数中加const则可以运行,void fun(const int& i){ }
——【例子3】
下面哪些选项能编译通过()(不定项)
int i;
char a[10];
string f();
string g(string &str);
A、if(!!I) {f();} B、g(f()); C、a = a + 1; D、g("ABC");
答案:A。
B、关键点时g++下临时变量都作为常量故f()返回值作为临时变量(常量)传递给函数g时出错,因为常量不能传递给一个非常量引用。详细如下:
#include <iostream> #include <string> using namespace std; //string的时候需要声明,忘了 string f() { return "a"; } //此时,只要修改为string g(const string &str) { },即在参数前加上const修饰就可以运行。 //string g(string &str) { } //不能运行 string g(const string &str) { } int main() { g(f()); return 0; }
C、a是常量,不可以进行赋值
D、同B。
3、static与const以及static const(const static)
(1)说明在C++中static、const以及static const成员变量的初始化?
解答:
在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义的外部,通常在类的实现文件中初始化,static关键字只能用于类定义内部的声明中,定义时不能标示为static。
在 C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。const数据成员只在某个对象生存期是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值时什么。(好像在C++11中可以在定义处初始化)
const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。详细如下:
class Test { public: Test(): a(0) { } enum { size1 = 100, size2 = 200}; private: const int a; static int b; const static int c; //与static const int c;相同,考虑c为整型时,有三种情况:第一,可在此处声明,但仍需在类定义体外进行定义初始化,因为const必须初始化。第二,直接在此处进行定义初始化,就不能在类定义体外进行赋值,const不能重复赋值。第三,绝对不允许在构造函数的初始化列表进行初始化。注意:c为非整型时,不能在此处初始化,整型包括char、short、long、int、float、double(但是string不行) }; int Test::b = 0; const int Test::c = 0;
【例子】
下面的()中可以用哪些选项填充,运行结果是:0 20 1 20?
#include <iostream> using namespace std; class Test { public: () int a; () int b; public: Test(int _a, int _b) : a(_a) { b = _b; } }; int Test::b; int main() { Test t1(0, 0), t2(1, 1); t1.b = 10; t2.b = 20; cout << t1.a <<" " << t1.b << " " << t2.a << " " << t2.b << endl; }
A、statc/const B、const/static C、-/static D、const static/static E、None of the above
答案:BC。发现t1与t2的b是相同的,仿佛“只有一份”,所以是静态的。相反,a肯定是非静态的。所以,a只是在初始化列表中初始化,const和非const都满足,因此答案是BC。
参考:
C++ primer 第四版
Effective C++ 3rd
http://blog.csdn.net/jnu_simba/article/details/9282235
http://blog.csdn.net/zjf280441589/article/details/24704603
C++编程规范
版权声明:本文为博主原创文章,未经博主允许不得转载。