1 / 26

Const

Const. 黄志超 软件 0902. C 中的 const. 问题 1 : const 变量 & 常量   为什么下面的例子在使用一个 const 变量来初始化数组, ANSI C 的编译器会报告一个错误呢?  const int n = 5; int a[n];. 变量 n 被修饰为只读变量,可惜再怎么修饰也不是常量。而 ANSI C 规定数组定义时维度必须是“常量” , “只读变量”也是不可以的。 那常量和只读常量有什么区别呢?

pello
Download Presentation

Const

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Const 黄志超 软件0902

  2. C中的const 问题1:const变量 & 常量   为什么下面的例子在使用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?  const int n = 5; int a[n];

  3. 变量n被修饰为只读变量,可惜再怎么修饰也不是常量。而ANSI C规定数组定义时维度必须是“常量” , “只读变量”也是不可以的。 那常量和只读常量有什么区别呢? 常量肯定是只读的,例如5, “abc”,等,肯定是只读的,因为常量是被编译器放在内存中的只读区域,当然也就不能够去修改它。而“只读变量”则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。 但在标准C++中,这样定义的是一个常量,这种写法是对的。

  4. 问题2:const变量 & const 限定的内容   下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?  typedef char * pStr; char string[4] = "abc"; const char *p1 = string; const pStr p2 = string; p1++; p2++; 问题出在p2++上

  5. 分析: typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。 • 由于p2不是指针,const 直接修饰到了p2,即现在的p2是常量了,它的类型是pStr(我们自己定义的类型),相当于const int p2, const long p2等等,const都是直接修饰p2的,只不过int,long是系统类型,而pStr是我们定义的类型。为什么会出现这种效果了,就是因为 typedef,它把char *定义成一个复合的类型,要从整体上来理解语义,而不是字符替换后来理解语义。

  6. 问题3:const变量 & 字符串常量  请问下面的代码有什么问题? char *p = "i'm hungry!"; p[0]= 'I'; 分析: “i'm hungry”实质上是字符串常量,而常量往往被编译器放在只读的内存区,不可写。

  7. 问题4:const & 指针 下面分别用const限定不可变的内容是什么? const int n;或者 int const n; const char *p; 或者 char const * p; char* const p; const char* const p; 或者 char const* const p; 答案: n是const *p是const, p可变 p是const,*p可变 p和*p都是const

  8. C++中的const 一、关于一般常量 const int bufSize = 512; 因为常量在定义后就不能被修改,所以定义时必须初始化。 二、关于数组及结构体 const int cntIntArr[] = {1,2,3,4,5}; struct SI { int i1; int i2; }; const struct SI s[] = {{1,2},{3,4}};

  9. 三、关于引用 const int i = 128; const int &r = i; //引用要在定义时初始化 普通引用不能绑定到const 对象,但const 引用可以绑定到非const 对象。 const int ii = 456; int &rii = ii; // error int jj = 123; const int &rjj = jj; // ok

  10. 非const 引用只能绑定到与该引用同类型的对象。 const 引用则可以绑定到不同但相关的类型的对象. 例如: double dVal = 3.1415; int &ri = dVal; //错误 const int &ri = dVal; //警告

  11. 四、关于指针 问题:以下两个有什么区别? • int i = 100; const int *p = &i; • int i = 100; int *const p = &i;

  12. 如何将一个const 对象合法地赋给一个普通指针??? 例如: • const double dVal = 3.14; double *ptr = &dVal; // error double *ptr = const_cast<double*>(&dVal); // ok: const_cast是C++中标准的强制转换, 在C语言中警告,使用: double *ptr = (double*)&dVal;

  13. 个人问题: 在C语言中: • const double d = 3.14; double *ptr =(double *) &d; *ptr = 5; printf( “*ptr = %f \n”, *ptr ); //*ptr = 5.000000 printf( “d = %f \n”, d ); //d = 5.000000 printf( “ptr = %p \n”, ptr ); //ptr = 0xbffcc92c printf( "&d = %p \n", &d ); //&d=0xbffcc92c 当把const double d = 3.14;作为全局变量时,出现段错误

  14. 在C++中: • const double d = 3.14; double *ptr = const_cast<double>(&d); *ptr = 5; printf( “*ptr = %f \n”, *ptr ); //*ptr = 5.000000 printf( “d = %f \n”, d ); //d = 3.140000 printf( “ptr = %p \n”, ptr ); //ptr = 0xbff94890 printf( "&d = %p \n", &d ); //d = 0xbff94890 • 当把const double d = 3.14;作为全局变量的时候出现段错误

  15. 2.const 指针(指针本身为常量) int i = 100; int *const p = &i; 指针的指向不能被修改。 *p = 1; //指针所指向的基础对象可以修改。 3.指向const 对象的const 指针(指针本身和指向的内容均为常量) const double pi = 3.14159; const double *const pi_ptr = &pi; 指针的指向不能被修改,指针所指向的基础对象也不能被修改。

  16. 假如涉及的是一级间接关系,则将非const指针赋给const指针是可以的。假如涉及的是一级间接关系,则将非const指针赋给const指针是可以的。 例如: int age = 39; int *pd = &age; const int *pt = pd; 不过进入两级间接关系时,与一级间接关系一样将const和非const混合的指针赋值方式将不再安全。 请看下面的代码,有什么问题吗? const int **pp2; int *p1; pp2 = &p1; //错误 分析: const int **pp2;整条语句应该这样翻译:pp2是一个指向指针对象的指针,而这个指针对象指向的又是一个int型的常量(const)。 把 int *p1; 改为const int *p1;则就可以通过编译。

  17. 五、关于一般函数 1.修饰函数的参数 class A; void f (const A &rA); // rA所引用的对象不能被修改 2.修饰函数的返回值 返回值:const int func1(); 返回引用:const int &func2(); // 注意千万不要返回局部对象的引用,否则会报运行时错误:因为一旦函数结束,局部对象被释放,函数返回值指向了一个对程序来说不再有效的内存空间。 返回指针:const int *func3(); // 注意千万不要返回指向局部对象的指针,因为一旦函数结束,局部对象被释放,返回的指针变成了指向一个不再存在的对象的悬垂指针。

  18. 六、关于类 class A { public: void func(); void func() const; const A operator+(const A &) const; private: int num1; mutable int num2; const size_t size; }; 1.修饰成员变量 const size_t size; // 对于const的成员变量,[1]必须在构造函数里面进行初始化;[2]只能通过初始化成员列表来初始化;[3]试图在构造函数体内对const成员变量进行初始化会引起编译错误。 例如: A::A( size_t sz ) : size( sz ) { }// ok:使用初始化成员列表来初始化 A::A( size_t sz ) { size = sz } // error

  19. 2.修饰类成员函数 void func() const; // const成员函数中不允许对数据成员进行修改.如果某成员函数不需要对数据成员进行修改,最好将其声明为const 成员函数,这将大大提高程序的健壮性。 const 为函数重载提供了一个参考 class A { public: void func(); // [1]:一个函数 void func() const; // [2]:上一个函数[1]的重载 …… }; A a(10); a.func(); // 调用函数[1] const A b(100); b.func(); // 调用函数[2]

  20. 为了确保const对象的数据成员不会被改变,在C++中,const对象只能调用const成员函数。如果一个成员函数实际上没有对数据成员作任何形式的修改,但是它没有被const关键字限定的,也不能被常量对象调用。下面通过一个例子来说明这个问题:为了确保const对象的数据成员不会被改变,在C++中,const对象只能调用const成员函数。如果一个成员函数实际上没有对数据成员作任何形式的修改,但是它没有被const关键字限定的,也不能被常量对象调用。下面通过一个例子来说明这个问题: class C { int X; public: int GetX() { return X; } void SetX(int X) { this->X = X; } }; void main() { const C constC; cout<<constC.GetX(); }   如果我们编译上面的程序代码,编译器会出现错误提示:constC是个常量对象,它只能调用const成员函数。虽然GetX( )函数实际上并没有改变数据成员X,由于没有const关键字限定,所以仍旧不能被constC对象调用。 将int GetX()改为int GetX() const 即可。

  21. 非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员:非常量成员函数不能被常量成员对象调用,因为它可能企图修改常量的数据成员: • public: • Set (void){ card = 0; } • bool Member(const int) const; • void AddElem(const int); • //... • }; • Set s; • s.AddElem(10); // 非法: AddElem不是常量成员函数 • s.Member(10); // 正确

  22. 3.修饰类对象 const A a; // 类对象a 只能调用const 成员函数,否则编译器报错。 4.修饰类成员函数的返回值 const A operator+(const A &) const; // 前一个const 用来修饰重载函数operator+的返回值,可防止返回值作为左值进行赋值操作。 例如: A a; A b; A c; a + b = c; // errro: 如果在没有const 修饰返回值的情况下,编译器不会报错。

  23. Const 与define的异同 • const与define两者都可以用来定义变量,但是const定义时,定义了常量的类型,而#define只是简单的文本替换。 • Const与define的最大的差别在于:前者在堆栈分配了空间,而后者只是把具体数值直接传递到目标变量。或者说const的常量是一个Run-Time的概念,它在程序中确确实实的存在并可以被调用、传递。而#define常量则是一个Compile-Time概念,它的生命周期止于编译期;在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。 • const常量存在于程序的数据段,#define常量存在于程序的代码段。const常量有数据类型,宏常量没有数据类型,编译器可以对const常量进行类型的安全检查。

  24. 七、const有什么主要的作用? 1.可以定义const常量,具有不可变性。 例如: const int Max=100; int Array[Max]; 2.便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。 例如: void f(const int i) { .........} 编译器就会知道i是一个常量,不允许修改; 3.可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变! 4.可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。

  25. 5.提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

  26. 给大家推荐一本书《c++ primer plus》 谢谢大家

More Related