一.相关知识点
在 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