C中const、static、staticconst和conststatic的初始化以及修改问题.docx_第1页
C中const、static、staticconst和conststatic的初始化以及修改问题.docx_第2页
C中const、static、staticconst和conststatic的初始化以及修改问题.docx_第3页
C中const、static、staticconst和conststatic的初始化以及修改问题.docx_第4页
C中const、static、staticconst和conststatic的初始化以及修改问题.docx_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C+中const 、static、 static const和const static的初始化以及修改问题本人系C+初学者,很渣很菜鸟,这也是本人写的第一篇有关C+的博文 ,说是写,不如说是对网络上高人博文的综合与整理。(当然,凡是本文涉及到别人博客内容的,都将附上网址链接。)写这篇博文的目的,是希望对相关知识做出尽可能全面、详尽、简易的解释,以供像我一样的初学者参考,同时也希望得到高人的批评与指正,以此来提高自己。另外,为保证文章的针对性,同时也限于本人水平,本文只对相关类型的数据做出讨论,并不涉及函数的讨论。下面,是我的一些整理与见解。一、const、static、static const、const static变量的初始化.const的初始化(1)只有这一种情况const变量可以不在声明的同时定义,那就是const变量作为类的数据成员出现时。例如:class Myclasscons int a;/注意,在任何情况下,const int a与int const a等价,只不过人们习惯写前者;但要注意,这样做是毫无意义的,只是编译能够通过罢了,int const a什么也做不了,因为它没有值。(2)凡是在函数(包括类中的,main函数及其它)中,const常量必须在声明时初始化,这是因为const被视为常量。例如:class Myclass()public:int test()const int b;当输入int const b;一句时,在分号下面出现红色下划线,鼠标移到该处,出现报错:Error:常量 变量 “b”需要初始值设定项。(注意,在本文中,以后这种情况我们简称为“下划线报错”)在main函数、全局函数中情况相似。(3)作为全局常量在全局中写const int b;/ 即不在类中,不在函数中写代码时不会出现下划线,但编译时报错:如果不是外部的,则必须初始化常量对象。注意这里的“外部的”是指声明为extern的常量。即将上句改为extern const int b;则可编译通过。因为声明为extern,就是在告诉编译器const int b是一个将在后面定义的变量。当然,如果只写extern const int b而不在其后对b赋值的话,也是毫无意义的(b没值,什么也干不了)。关于xetern的详细情况,见注释。 (3)如何初始化1)作为类的数据成员只能通过构造函数的初始化列表来初始化。注意在构造函数的函数体内初始化是不行的,见下例:class Myclass public:Myclass() /在大括号下面出现下划线报错:Error:Myclass :Myclass()未提供初始值设定项。这是因为类中有const常量所以编译器提示在写构造函数时要提供初始化列表。b=1; /b下下划线报错:Error:表达式必须是可修改的左值private:const int b;;对于这种情况,我们可以试着这样解释: b=1;写在函数体内,被看成是一条赋值语句,(这从报错“表达式必须是可修改的左值”可以看出)而b是常量,这当然是不允许的。正确做法:#include using namespace std;Class Myclass public:Myclass(int bz):(b)bz;int Getdata()return b;private:const int b;void main() Myclass obj(1);coutobj. Getdata()endl;/输出12)其它地方的const常量都可以直接初始化,即const int b=1;都可以。最后,关于const ,再简单说两点。为什么不能在类内初始化const?关于这一点,网上最普遍的说法是:const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。但依本人愚见,更为主要的原因是:const数据成员是类内(in_class)成员(即类的不同对象中const数据成员的值可以不同)const int b;与int a;的不同仅在于,在一个对象中,a可以改b不可以改。无论对a还是b,初始化意味着为a和b分配内存,而我们知道,类是抽象的,并不占用内存,编译器编译时,根据类的数据成员计算出类的大小,但不进行内存分配操作(见注释)。只有在实例化对象时,才为对象分配内存。如果初始化数据成员,一方面,初始化要分配内存,另一方面,声明类不分配内存,这显然是矛盾的。另外,对多个对象而言,const是变量,如果在类内初始化const的话,那么由该类创建的多个对象中的const相同,这和我们的初衷是相违背的。总之,在下个人认为,不能在类内初始化const与不能初始化int a的道理是一样的。前面我们说过,类中的const数据成员在该类的不同对象间是可变的,及同一类的不同对象中可以不同。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。关于static const,后面将详细讨论,至于枚举常量本文所讨论的重点,我们也只给出一个简单的例子。class Myclassprivate:enum size1=100, size2 = 200 ;;这里只给出初始化的形式,至于为什么size1、size2是整个类中都恒定的常量,就不给出验证的代码了,本人在这一块也不精通,但可以告诉大家,对size1、size2进行再赋值,取地址等操作是不可以的,编译器直接报错。有兴趣的读者可以自行深入探究,当然也欢迎将结果告知在下。本小节最后,向大家推荐一篇文章const的思考,(百度可见)本人感觉这篇文章相对来说写得比较专业,同时全面易懂,应该出自大神之笔,大家看了应该会有所收获。static的初始化1)首先,浅析一下static数据。static数据,即静态数据,它有以下特点:全局性:static(无论局部static还是全局static)分配在静态(或称全局)存储区, 在程序整个运行期间都不释放.(但要注意:见注释)初始化一次性: 无论是静态局部变量还是静态全局变量,都只初始化一次。记忆性:所谓”记忆性”是指在两次函数调用时, 在第二次调用进入时, 能保持第一次调用退出时的值,直至重新赋值。static具有全局唯一性的特点, 每次调用时, 都指向同一块内存。eg.#includeusing namespace std;void ceshi1()for(int i=0;i3;i+)int k=0;k+;coutkendl;void ceshi2() for(int i=0;i3;i+)static int t=0;t+;couttendl;void main () ceshi1();/输出1 1 1ceshi2();/输出1 2 3作用域限定性:函数或变量前加static使得函数成为静态函数,变量成为静态变量。此时,函数的作用域仅局限于本文件(所以又称内部函数)。注意,此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区, 生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.以上参考自/Kendiv/article/details/675941,读者可进一步了解更多细节。特别的,对类的static数据成员,有静态数据成员有以下特点: 对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共用。 即静态数据成员独立于对象之外,放在一个单独的区域(静态数据区),为同一类的所以对象共享。(见例1、例4) 静态数据成员的值对每个对象都是一样的,但它的值可以改,一改全改;比如,原来,在类中声明ctatic int a;在类外初始化为1,则在该类的每一个对象中,a值为1,后来,将a该为2,则此时,在该类的每个对象中,a值为2;(见例4) 静态数据成员存储在静态数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义(即初始化)。(见例2、例3) 静态数据成员和普通数据成员一样遵从public, protected, private访问规则,private, protected 的static成员虽然可以在类外初始化,但是不能在类外被访问。 static成员变量的初始化是在类外,因为static变量不是类的非静态数据成员,注意此时不能再带上static的关键字。(见例2、例3、例4) 因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它; (见例5) 若不对静态数据成员初始化,系统自动将int型初始化为0,char型自动初始化为ASSIC码值为0的字符(NULL),但一定注意,此时初始化语句还是要写,只不过不赋右值,例如int Myclass:a;(见例5) 静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:数据类型类名:静态数据成员名=值 类的静态数据成员有两种访问形式:类对象名.静态数据成员名 或 类类型名:静态数据成员名如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员 ; 静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次,则所有存款类对象的利息全改变过来了; 同全局变量相比,使用静态数据成员有两个优势:1. 静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性; 2. 可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;以上总结来自/jsjwql/article/details/1788286 。例1:#includeusing namespace std;class Myclasspublic:Myclass(int a=0,char b=0,float c=0 )private:int a;char b;float c;static int d;int Myclass:d=1;void main()Myclass obj(1,a,1.0);coutsizeof(obj)endl;/输出12由此可以看出,对象obj指向的内存的大小就是int a、char b、float c的大小,不包括static int d,由此可知static不属于具体对象。其实,前三者存在栈区,后一者存在静态数据区。至于char b占一个字节,为什么输出是12,这是编译系统进行内存对齐的缘故。至于对齐,还是那句话,不是重点不展开,相信读者自己能够解决。例2:(这里我们只给出代码段,说明问题即可)class Myclasspublic:Myclass(int az):a(az)/a下下划线报错:Error:“a”不是类“Myclass”的非静态数据成private: /或基类static int a;/也可以写成int static a;这说明不能通过构造函数初始化列表初始化static数据成员。例3:#includeusing namespace std;class Myclasspublic:Myclass(int az) /企图通过构造函数,在函数体内对static int a 初始化a=az;int re_a()return a;private:static int a;void main ()Myclass obj(1);coutobj.re_a()endl;写代码时不会出现下划线报错,但编译过不去,有类似“一个无法解释的外部命令”的报错。例4:#includeusing namespace std;class Myclasspublic:int re_a()return a;void chong_fu_zhi()a=2;private:static int a;int Myclass:a=1;/static int Myclass:a=1;/如果这样写,会出现下划线报错:Error:此处不能指定存储类void main ()Myclass obj1,obj2;coutobj1.re_a()endl;/输出1coutobj2.re_a ()endl;/输出1obj1.chong_fu_zhi();/注意这里只是修改obj1的a,但a值一改全改,这恰说明acoutobj1.er_a i()endl;/输出2 /为所有对象共享coutobj2.re_a ()endl;/输出2例5:#includeusing namespace std;class Myclasspublic:static int a;static int b;static char c;int Myclass:a=1;int Myclass:b;int Myclass:c;void main()cout Myclass:aendl;/输出1 /未实例化对象就可以对static 数据进行操作cout Myclass:bendl;/输出0cout Myclass:aendl;/输出空格(ASSIC码值为0的是NULL)2)如何初始化前面说的够多了,见例4就可以了。.static const和const static的初始化1)简单说明首先需要说明,以本人的认识和经验,static const和const static在使用上没有什么区别,可以看作同一类型的两种写法。一个是静态常量,一个是常量静态,都兼具了static和const的特点,把握好了这一点,下面的内容就不难理解了。关于这两者,我们就不过多的介绍了,其实也没什么可介绍的了,大家知道它们既有static的特点又有const的特点就行了,下面我们来说明它们的初始化。2)如何初始化.先说普遍情况类外(包括全局、普通函数、main函数中)直接初始化,即static const int a=1; /当然也可以写成int static const,static static const float b= 1.1; /const也可以互换位置。 等都可以。类内的函数中(普通成员函数,构造函数中)同,可以直接初始化。注意,说在构造函数中可以,是说类似static const floatb= 1.1;这样的语句可编译通过,并不是说在类内可以对static const数据成员初始化 ,当然,我还没发现在构造函数中写类似static const float b= 1.1这样的语句有什么实际用途,只是知道这样写是可以通过编译的。举一小例:#includeusing namespace std;class Myclasspublic:float ceshi()static const float c=1.6f;/ (*)注意这里的f,后面解释return c;void main() Myclass obj;coutobj.ceshi()endl;/输出1.6简单说一下(*)句的f,因为编译器将1.6默认为double,若不加f(或F),会出现警告:从“double”到“const float”截断,当然不影响运行,我想大家知道就可以了。作为类的数据成员前面说过,static const兼有static、const的双重特点,故不能初始化static、const数据成员的形式,自然不能拿来初始化static const。即不能直接初始化(当然不包括后面讲到的特例),也不能在构造函数中初始化(无论是在初始化列表还是在函数体中)。由于情况与前边十分类似,这里不再给出代码来举例说明。既然如此,那就只好在类外初始化,形式同static。eg.#includeusing namespace std;class Myclasspublic:float Get_a() return a;float Get_b() return b;private:static const float a; const static float b; const float Myclass:a=1.6f;/ f的事情已经解释过 /见注释 const float Myclass:b=2.1f; void main() Myclass obj;coutobj.Get_a()endl;/输出1.6coutobj.Get_b()endl;/输出2.1.再说明特殊情况static const int型 和static const char型数据成员可以在类内初始化!eg.#includeusing namespace std;class Myclasspublic:float Get_a() return a;char Get_t() return t;private:static const int a=1; static const char t=a;void main() Myclass obj;coutobj.Get_a()endl;/输出1coutobj.Get_t()endl;/输出a如果在private下写static const float a=1.1;是不行的,等号下下划线报错:Error:“const float”类型的成员不能包含“in-class initializer”,即不能在类内初始化。编译报错:只有静态常量整型数据成员才可以在类中初始化!至于原因,在下在网上找到的最有说服力的答案是:=C+11 之前的标准 只有静态常量整型数据成员,才可以在类中初始化。 这是因为,当时认为,类定义中的数据定义,是一种声明,不是数据定义。 当用类定义对象(变量,常量)时候,才开始定义数据。 静态常量整型数据成员1)不是对象的一部分2)可以产生常量表达式,所以可以在类中初始化。-否则,用它作为数组的大小,就不合适了。 静态常量整型数据成员,能够用来当作常量表达式使用, 不在内部定义的话,则该常量表达式未定义,就不能使用了。C+11 非静态成员变量(常量),可以直接初始化,或者在初始化表中初始化。C+11 中 初始化非静态成员变量(常量),可以看作赋给变量(常量)一个默认值。=除此之外,本人补充一下:static const int a=1;并不分配内存,编译时直接将a换成1,放到常量表中(关于常量表,本人不是很清楚,读者可以百度,有知道的欢迎告诉我),当然,若对a取地址,则在只读的常量区分配内存。所以,这和类声明不分配内存并不矛盾。至于为什么char型数据成员也可以,我想着应该与char的实现机制有关,char型可以转换成int型,毕竟我们知道字符可以以int型输出其ASSIC码值。不知道我的想法是否正确,欢迎高人批评指正。 而至于为什么别的类型(比如float)不可以在类内初始化,是因为他们不能像int那样直接进行常量替换,而为什么不能直接进行常量替换,我能给出的解释只能是C+的机制问题,此时不妨参考上面高人给出的解释,从为什么int能来从反面理解为什么其它类型不能。另外,从C+11允许对所有非静态成员变量(常量)在类内初始化更可以看出这是C+本身机制的问题。还是那句话,希望高人指点迷津。对于高人说的“常量表达式”和“数组”,我想可从下面的例子了解一二。例如,在类的private下写: static const int n=10;int strn;/声明数组不会下划线报错,鼠标移到str下,显示int myclass:str10;但如果在Myclass类内的private下写static const int n;int strn;在类外初始化const int Myclass:n=10;则int strn;一句中n下下划线报错:Error:表达式必须含有常量值由此可对高人的话有所理解。但同时,我想说,我觉得通过n来声明数组完全没必要,因为n是不能被修改的,那这样还不如直接写int str10;从而省去了声明、定义n的事。 或许我举的例子不够恰当,没能体现出那位高人的本意。还有一点在下想说,那就是关于static const int的事,本人纠结了很久了,包括为什么在类内可以直接初始化它,怎么用,有什么好处,编译器到底为不为它分配内存,什么情况下进行常量折叠,等等等等。我查阅了很多资料(包括国外的编程网站),也请教了很厉害的老师 ,但最终也没能完全弄清楚。一来是在下水平低,二来是各种说法让我莫衷一是,有的说法我可以自己编程验证,但有的暂时还不知道好如何验证。现在只搞清楚了(算是清楚了吧)一点儿,那就是从汇编来看,static const int a=1;这句无论写在哪里,都不会分配空间,但写在函数中(无论什么函数),并把return a时,将会对a分配内存,当然,在任何地方对a取地址或引用a时,也将对a分配空间。(读者可以参考我前边提到的const的思考,里面比较详尽地给出了什么时候给const数据分配内存,我想static const也是如此)当直接使用a时,例如int d;d=a;或cout Myclass:f;(f为类Myclass下的public的static const int),都不分配空间。还有,有人说static const int n=30;与#define n 30作用类似,但前者优于后者,原因是前者在使用时只分配一次内存,后者每次使用时都分配内存,但在下经过试验貌似不是这样,两者在直接使用时都不分配内存,通过函数return时,都将分配内存。(以上关于内存分配的讨论很可能有错,因为本人对汇编并不太懂)本人感觉前者的优点主要是可以封装在类中,实现了C+的封装性。 这就是在下所知道的。所以,在下在这里虔诚呼吁,凡是对以上问题明白的,哪怕是略知一二,也万望您不吝赐教。好了,至此,关于初始化的问题就说到这里,其中也扯进来一些相关问题,可能让读者感到烦乱,而在下这样做的目的主要是把在下知道的都与大家分享,希望让像我一样的初学者能更明白地,较为全面地学到一些东西;同时多多暴露自己的问题和错误,以期在大家的批评指正下提高。由此给您造成的阅读不便,敬请谅解。下面,在简单说一下const、 static 、static const的值的修改问题。二、const、static、static const、const static常量(变量)值的修改本节讨论主要参考自:/taotaotheripper/article/details/23712253 不能修改的全局的const 、任意地方的static const不能修改。原因:无论是否是静态,两者都是常量,显然不能通过简单的再赋值操作,即类似static const int a=1;a=2;的方式修改。那么在下能想到的办法只有通过指针来修改了。对于全局的const 、任意地方的static const,不分配内存,但一旦取地址(或是函数中return,或是将const声明为extern const,都将导致分配内存),那么在哪儿分配呢?是在常量存储区,这一区域的特点是只读,当然不能通过指针修改。例如在外部(类外、函数外的全局)写:const int a=1;static const int b=1;在main函数中写int*pa=const_cast(&a);/见注释 *pa=111; /(1) int*pa=const_cast(&a);*pa=111;/(2)编译可通过,但出现运行时错误,最终可见类似 jing_tai_cheng_yuan.exe 中的 0x0106162b 处有未经处理的异常: 0xC0000005: 写入位置 0x01067830 时发生访问冲突这样的报错。即访问非法内存,错误指向(1)、(2)两句。因为如前所说,取地址后,a被分到了只读的常量区。.其它情况下的const值的修改先看这个例子:#includeusing namespace std;class Chang_liangpublic:Chang_liang(int az):a(az)int*pa=const_cast(&a);*pa=111;cout类的数据成员:endl;couta=aendl;cout*pa=*paendl;couta的地址:&aendl;cout*pa的地址:paendl;void ce_shi()const int b=1; int*pb=const_cast(&b);*pb=111;cout类内函数中:endl;coutb=bendl;cout*pb=*pbendl;coutb的地址:&bendl;cout*pb的地址:pbendl;private:const int a;void lei_wai() const int c=1; int*pc=const_cast(&c); *pc=111; cout类外普通函数中:endl;coutc=cendl;cout*pc=*pcendl;coutc的地址:&cendl;cout*pc的地址:pcendl;void main()const int d=1;int*pd=const_cast(&d); *pd=111;Chang_liang obj(1);obj.ce_shi();lei_wai();coutmain函数中:endl;coutd=dendl;cout*pd=*pdendl;coutd的地址:&dendl;cout*pd的地址:pdendl;运行结果:类的数据成员:a=111*pa=111a的地址:010BFA74*pa的地址:010BFA74类内函数中:b=1*pb=111b的地址:010BF988*pb的地址:010BF988类外普通函数中:c=1*pc=11c的地址:010BF994*pc的地址:010BF994main函数中:d=1*pd=11d的地址:010BFA8C*pd的地址:010BFA8C我们可以看到,运行没有问题,指针指向的地址与数据指向的地址是一样的,指针指向的区域(也就是数据所在地址的内容)确实被修改了,(关于这一点,请看大神文章:/content/12/0824/20/8093902_232153101.shtml )但b、c、d的输出仍然是1!这就涉及到所谓的“常量折叠”的问题。即在函数(从上例可见无论什么函数)中定义的const常量,在程序编译期间就直接替换成具体的数(如将b换成1),放到符号表中,以后凡是用到b的地方,直接用1代替,根本不经过读内存操作,所以,哪怕对b取地址使编译器为b分配了内存,运行时b所在地址的内容确实改变了,但coutb就是cout1,这在编译期间就决定好了!正因为如此,才体现了const的含义。那么,类中的数据成员const a为什么就被修改了呢?前边说过,类的数据成员const int b与int a的区别仅在于在一个具体的对象中,b不能改变(当然也有办法改,比如本例)而a可以改变。在实例化对象时,b并不进行常量折叠,而是和a一样在栈区分配内存,所以通过指针修改它是可以的。.static值的修改static修饰的变量,虽然是静态的,但毕竟是变量,满足变量的特征,(const和static const是常量,具有常量特征)存储在静态(全局)存储区(无论是静态全局变量还是静态局部变量)。所以,对static变量的值的修改,通过再赋值操作就可以了无论什么地方(类的数据成员,各种函数中,全局)的static。例如a=111;(a为已经初始化为1 的static int),则a被改成111,修改方法和道理与修改int a是一样的。变量的修改没有什么讨论的意义和价值,在此不再多说,也不给出示例代码。有疑问的读者,可以自己编程验证。由于本文中多次涉及到C+的内存分配问题,所以在注释中给出C+内存分配的简介,以供读者学习参考。至此,本人的这篇文章就结束了,写得比较乱,比较杂,为此在此想读者致歉。由于本人水平有限,错误疏漏之处在所难免,希望各位读者批评指正,不吝赐教,本人将不胜感激!谢谢!注释:关于extren,分两种情况简单说明:1)在同一文件中extern

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论