一.相关知识点
在 C语言中可以选择这样书写:
二.相关代码实现
1.
<span style="font-family:SimSun;font-size:18px;"><strong>/*const的安全性(const默认内部连接) const的作用不限于在常数表达式里代替 #defines。如果用运行期间产生的值初始化 一个变量而且知道在那个变量寿命期内它是不变的,用 const限定该变量,程序设计 中这是一个很好的做法。如果偶然改变了它,编译器会给出一个出错信息。*/ /*SAFECONS.cpp*/ #include <iostream> using namespace std; const int i = 100;//typical constant const int j = i + 10;//value from const expr long address = (long)&j;//forces storage char buf[j + 10];//still a const expression int main() { cout << "type a character & CR:"; const char c = cin.get();//can't change const char c2 = c + 'a'; cout << c << endl; cout << c2 << endl; //... return 0; } </strong></span>
<span style="font-family:SimSun;font-size:18px;"><strong>/*CONSTVAL.cpp*/ /*返回const值 对返回值来讲,存在一个类似的道理,即如果从一个函数中返回值,这个值作为一个常 量: const int g(); 约定了函数框架里的原变量不会被修改。正如前面讲的,返回这个变量的值,因为这 个变量被制成副本,所以初值不会被修改。 首先,这使 const看起来没有什么意义。可以从这个例子中看到:返回常量值明显失 去意义:*/ #include <iostream> using namespace std; int f3() { return 1; } const int f4() { return 1; } int main() { const int j = f3(); int k = f4(); return 0; }</strong></span>
<span style="font-family:SimSun;font-size:18px;"><strong>/*CONSTRET.cpp*/ /*对于内部数据类型来说,返回值是否是常量并没有关系,所以返回一个内部数据类 型的值时,应该去掉const从而使用户程序员不混淆。 处理用户定义的类型时,返回值为常量是很重要的。如果一个函数返回一个类对象的 值,其值是常量,那么这个函数的返回值不能是一个左值(即它不能被赋值,也不能 被修改)。例如:*/ #include <iostream> using namespace std; class X { int i; public: X(int I = 0); void modify() { i++; } }; X f5()//f5()返回一个非const X对象 { return X(); } const X f6()//f6()返回一个const X对象 { return X(); } void f7(X &x)//函数f7()把它的参数作为一个非const引用从效果上讲,这与取一个 //非const指针一样,只是语法不同。 { x.modify(); } int main() { //只有非const返回值能作为一个左值使用。换句话说,如果不让对象的返回值 //作为一个左值使用,当返回一个对象的值时,应该使用const。 f5() = X(1); f5().modify(); f7(f5()); //!f6() = X(1); //!f6().modify(); //!f7(f6()); return 0; } /*我们可以把一个非const对象的地址赋给一个 const指针,因为也许有时不想改变 某些可以改变的东西。然而,不能把一个const对象的地址赋给一个非 const指针, 因为这样做可能通过被赋值指针改变这个 const指针。当然,总能用类型转换强制 进行这样的赋值,但是,这不是一个好的程序设计习惯,因为这样就打破了对象的 const属性以及由const提供的安全性。*/</strong></span>
<span style="font-family:SimSun;font-size:18px;"><strong>/*传递和返回地址 如果传递或返回一个指针(或一个引用),用户取指针并修改初值是可能的。如果使 这个指针成为常(const)指针,就会阻止这类事的发生,这是非常重要的。事实上, 无论什么时候传递一个地址给一个函数,我们都应该尽可能用 const修饰它,如果不 这样做,就使得带有指向const的指针函数不具备可用性。 是否选择返回一个指向const的指针,取决于我们想让用户用它干什么。下面这个例子 表明了如何使用const指针作为函数参数和返回值:*/ /*CONSTP.cpp*/ #include <iostream> using namespace std; void t(int*)//函数t()把一个普通的非const指针作为一个参数 {} void u(const int* cip)//函数u()把一个const指针作为参数 { //!*cip = 2;//illegal--modifies value int i = *cip;//OK--copies value //可以把信息拷进一个非const变量 //!int *ip2 = cip;//illegal:non-const } const char* v()//函数v()返回一个从串字面值中建立的const char* { return "result of function v()"; } const int* const w()//w()的返回值要求这个指针及这个指针所指向的对象均为常量 { static int i; return &i; } int main() { int x = 0; int* ip = &x; const int* cip = &x; t(ip);//OK //!t(cip);//not OK u(ip);//OK u(cip);//OK //!char* cp = v();//not OK const char* ccp = v();//OK //!int* ip2 = w();//not OK const int* const ccip = w();//OK const int* cip2 = w();//OK //编译器拒绝把函数w()的返回值赋给一个非const指针,而接受一个const int* //const,但令人吃惊的是它也接受一个const int*,这与返回类型不匹配。正 //如前面所讲的,因为这个值(包含在指针中的地址)正被拷贝,所以自动保持 //这样的约定:原始变量不能被触动。因此,只有把 const int*const中的第二 //个 const当作一个左值使用时(编译器会阻止这种情况),它才能显示其意义。 //!*w() = 1;//not OK return 0; }</strong></span>
5.
<span style="font-family:SimSun;font-size:18px;"><strong>/*常量的引用*/ /*CONSTTMP.cpp*/ #include <iostream> using namespace std; //临时变量通过引用被传递给一个函数时,这个函数的参数一定是常量(const)引用 class X {}; X f() { return X(); } void g1(X&) {} void g2(const X&) {} int mian() { //!g1(f());//Error:const temporary created by f() g2(f());//OK:g2 takes a const reference return 0; } //函数f()返回类X的一个对象的值。这意味着立即取f()的返回值并把它传递给其他函 //数时(正如g1()和g2()函数的调用),建立了一个临时变量,那个临时变量是常量。 //这样,函数 g1()中的调用是错误的,因为g1()不带一个常量(const)引用,但是 //函数g2()中的调用是正确的。</strong></span>
6.
<span style="font-family:SimSun;font-size:18px;"><strong>/*在一个串指针栈里的 enum的用法*/ /*SSTACK.cpp*/ /*注意push()带一个const char*参数,pop()返回一个const char*, stack保存 const char*。如果不是这样,就不能用 StringStack保存iceCream里的指针。然而, 它不让程序员做任何事情以改变包含在StringStack里的对象。当然,不是所有的串 指针栈都有这个限制。虽然会经常在以前的程序代码里看到使用 enum技术,但C++还 有一个静态常量static const,它在一个类里产生一个更灵活的编译期间的常量。*/ #include <string.h> #include <iostream.h> class StringStack { enum{size = 100}; const char* stack[size]; int index; public: StringStack(); void push(const char* s); const char* pop(); }; StringStack::StringStack():index(0) { memset(stack, 0 ,size * sizeof(char*)); } void StringStack::push(const char* s) { if(index < size) { stack[index++] = s; } } const char* StringStack::pop() { if(index > 0) { const char* rv = stack[--index]; stack[index] = 0; return rv; } return 0; } const char* iceCream[] = { "pralines & cream", "fudge ripple", "jamocha almond fudge", "wild mountain blackberry", "raspberry sorbet", "lemon swirl", "rocky road", "deep chocolate fudge" }; const ICsz = sizeof(iceCream)/sizeof(*iceCream); int main() { StringStack SS; for(int i = 0; i < ICsz; ++i) { SS.push(iceCream[i]); } const char* cp; while((cp = SS.pop()) != 0) { cout << cp << endl; } return 0; } </strong></span>
7.
<span style="font-family:SimSun;font-size:18px;"><strong>/*比较const和非const成员函数*/ /*QUOTER.cpp*/ #include <iostream.h> #include <stdlib.h> #include <time.h> class quoter { int lastquote; public: quoter(); int Lastquote() const; const char* quote(); }; quoter::quoter() { lastquote = -1; time_t t; srand((unsigned)time(&t));//Seed generator } int quoter::Lastquote() const { return lastquote; } const char* quoter::quote() { static const char* quotes[] = { "Are we having fun yet?", "Doctors always know best", "Is it ... Atomic?", "Fear is obscene", "There is no scientific evidence" "to support the idea" "that life is serious", }; const qsize = sizeof(quoters)/sizeof(*quotes); int qnum = rand() % qsize; while(lastquote >= 0 && qnum == lastquote) { qnum = rand() % qsize; } return quotes[lastquote = qnum]; } int main() { quoter q; const quoter cq; cq.Lastquote();//OK //!cq.quote();//not OK;non-const function for(int i = 0; i < 20; ++i) { cout << q.quote() << endl; } return 0; } /*构造函数和析构函数都不是const成员函数,因为它们在初始化和清理时,总是对对 象作些修改。quote()成员函数也不能是const函数,因为它在返回说明里修改数据成 员lastquote。然而Lastquote()没做修改,所以它可以成为const函数而且也可以被 const对象cq安全地调用。*/</strong></span>
8.
<span style="font-family:SimSun;font-size:18px;"><strong>/*在const成员函数里改变数据成员 第一种方法已成为过去,称为“强制转换 const”.它以相当奇怪的方式执行。取this (这个关键字产生当前对象的地址)并把它强制转换成指向当前类型对象的指针。看 来this已经是我们所需的指针,但它是一个const指针,所以,还应把它强制转换成 一个普通指针,这样就可以在运算中去掉常量性。*/ /*CASTAWAY.cpp*/ #include <iostream> using namespace std; class Y { int i, j; public: Y() { i = j = 0; } void f() const; }; void Y::f() const { //!i++;//Error -- const member function ((Y*)this)->j++;//OK -- cast away const-ness } int main() { const Y yy; yy.f();//actually changes it! return 0; } //问题:this没有用const修饰,这在一个对象的成员函数里被隐藏,这样,如果用户 //不能见到源代码(并找到用这种方法的地方),就不知道发生了什么。</strong></span>
9.
<span style="font-family:SimSun;font-size:18px;"><strong>/*为解决所有这些问题,应该在类声明里使用关键字mutable,以指定一个特定的数据 成员可以在一个 const对象里被改变*/ /*在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将 永远处于可变的状态,即使在一个const函数中。*/ /*MUTABLE.cpp*/ #include <iostream> using namespace std; class Y { int i; mutable int j; public: Y() { i = j = 0; } void f() const; }; void Y::f() { //!i++;//Error -- const member function j++;//OK:mutable } int main() { const Y yy; yy.f();//actually changes it! return 0; }</strong></span>
10.
<span style="font-family:SimSun;font-size:18px;"><strong>/*类涉及到硬件通信*/ /*VOLATILE.cpp*/ /*就像const一样,我们可以对数据成员、成员函数和对象本身使用 volatile,可以 并且也只能为volatile对象调用volatile成员函数。*/ #include <iostream> using namespace std; class comm { const volatile unsigned char byte; volatile unsigned char flag; enum { bufsize = 100 }; unsigned char buf[bufsize]; int index; public: comm(); void isr() volatile; char read(int Index) const; }; comm::comm() :index(0), byte(0), flag(0) {} void comm::isr() volatile { if(flag) { flag = 0; } buf[index++] = byte; if(index >= bufsize) { index = 0; } } char comm::read(int Index) const { if(Index < 0 || Index >= bufsize) { return 0; } return buf[Index]; } int main() { volatile comm Port; Port.isr();//OK //!Port.read(0);//Not OK read() not volatile return 0; }</strong></span>
三.习题+解答
1. 建立一个具有成员函数 fly ()的名为bird的类和一个不含fly()的名为rock的类。建立一个rock对象,取它的地址,把它赋给一个 void*。现在取这个void *,把它赋给一个bird*,通过那个指针调用函数fly()。 C语言允许公开地通过void*赋值是C语言中的一个“缺陷”,为什么呢?您知道吗?
#include <iostream> using namespace std; class bird { public: bird(); void fly(); }; class rock { public: rock(); }; bird::bird() {} rock::rock() {} void bird::fly() { cout<< "bird can fly!" << endl; } int main() { rock r; void* rv= &r; bird* b= (bird*)rv; b->fly(); return 0; }
2. 建立一个包含 const成员的类,在构造函数初始化表达式表里初始化这个 const成员,建立一个无标记的枚举,用它决定一个数组的大小。
#include <iostream> using namespace std; class A { const int i,j; enum { size = 100 }; unsigned char arr[size]; public: A(); }; A::A():i(0),j(0) {}; int main() { A a; return 0; }
#include <iostream> #include <string> using namespace std; class B { string splay; public: B(); string play() const; const char* go(); }; B::B():splay("happy!") { cout << "Created!" << endl; } string B::play() const { return splay; } const char* B::go() { static const char* go[] = { "playing the computer game", "watching TV", "playing badminton" }; const qsize = sizeof(go)/sizeof(*go); int qnum = rand() % qsize; return go[qnum]; } int main() { B b; const B cb; cout << b.go() << endl; cout << b.play() << endl; cout << cb.play() << endl; return 0; }
#include <iostream> using namespace std; class Table { char c; mutable int i; public: Table() { c = '0'; i = 0; } void f() const; }; void Table::f() const { i++;//OK:mutable } int main() { const Table t; t.f();//actually changes it! return 0; }
为解决所有这些问题,应该在类声明里使用关键字mutable,以指定一个特定的数据成员可以在一个 const对象里被改变在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将
永远处于可变的状态,即使在一个const函数中。
5. 请自行证明C和C++编译器对于const的处理是不同的。创建一个全局的const并将它用于一个常量表达式中;然后在C和C++下编译它。
C
#include <stdio.h> const int i = 100; const int j = i + 10;//error C2099: initializer is not a constant int main() { int num; num = 2*j; printf("num = %d\n",num); return 0; }
#include <stdio.h> int main() { const int i = 100; const int j = i + 10; int num; num = 2*j; printf("num = %d\n",num); return 0; }
C++
#include <iostream> using namespace std; const int i = 100; const int j = i + 10; int main() { int num; num = 2*j; cout << "num = "<< num << endl; return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/qaz3171210/article/details/47150281