经过半个月的辛苦的学习,终于把那个该死的科目三给考过去了,真是太煎熬啊。又被晒黑了,不知道又要捂多久才能重新变过来。。。不过,好在自己抽空能够看下C++时间也不算太难过。马上要回学校了,一看自己才写了三篇博文,简直不能忍了。最近要加把油啊,一会学校事情可就多了。
1.1 文件尾条件
如果输入来自于文件,则存在一种强大的技术来检测文件尾———检测文件尾EOF。C++输入工具和操作系统一起来检测文件尾,并将其告知程序。乍一看,读取文件中的信息cin似乎和键盘输入没什么关系,但其实有两个相关的关系,首先很多操作系统都支持重定向,允许文件代替键盘输入;其次,很多操作系统都允许通过键盘来模拟文件尾条件。在Unix中可以在首行按下Ctrl+D来实现,在Windows下可以在任意位置按下Ctrl+Z和Enter。总之,很多PC编程环境都将Ctrl+Z视为模拟的EOF,但具体细节(在什么位置以及是否需要按下Enter)各不相同。
检测到EOF后,cin将两位(eofbit与faiblt)都设置为1 。如果检测到EOF,则cin.eof()将返回bool值true,否则返回false。同样,如果eofbit或failbit被设置为1,cin.fail()也会返回1 。因此将cin.fail()与cin.eof()测试放到读取后以检测文件。
<pre class="cpp" name="code">include <iostream>
using namespace std;
int main()
{
char ch;
int count=0;
cin.get(ch);
</span>
<span style="font-size:18px;"> while(cin.eof()==false)
{
cout<<ch;
++count;
cin.get(ch);
}
cout<<"The num is "<<count<<endl;
}
</span>
只有当你键入Ctrl+Z与Enter时程序才会终止。
1.2 另一个cin.get()版本
通过与上面的cin.get()相对比,不接受任何参数的该函数会返回输入中的下一个字符。也就是说可以这样使用它
<pre class="cpp" name="code">ch=cin.get();
当然,对应于cin.put(),此函数同样有两种用法来显示字符
ch=cin.put;
cin.put(ch);
当该函数位于文件尾时,即EOF,cin.get()它将会返回一个特殊的值。改常量是定义在头文件iostream中的。通常EOF被定义为-1,因为在ASCII中并没有-1的字符,所以只需要让程序知道就可以了。所以上述代码也可以这样写
<span style="font-size:18px;">while((ch=cin.get())!=EOF)
{
cout.put(char(ch));
++count;
}</span>
在这里结合1.1 与1.2 来对其做一个小结
属性 | cin.get(ch) | ch=cin.get() |
传递输入字符的方式 | ||
用于字符输入时的函数返回值 | istream对象 | int类型的字符编码 |
到达EOF时的返回值 | istream对象 | EOF |
1.3 字符函数库cctype
C++从C语言中继承了一个与字符相关的函数软件包,他可以简化诸如确定字符是否为大写字母、数字、标点符号等工作,这些文件是在头文件cctype中定义的。这里来对其中的一些函数进行总结。
函数名称 | 返回值 |
isalnum() | 如果参数是字母或者是数字,该函数返回true |
isalpha() | 如果参数是字母,则会返回true |
iscntrl() | 如果参数是控制字符,则会返回true |
isdigit() | 如果参数是数字0~9,则会返回true |
isgraph() | 如果参数是如空格之外的打印字符,将会返回true |
islower() | 如果参数是小写字母,则会返回true |
isprint() | 如果参数是打印字符(包括字符),则会返回true |
ispunct() | 如果参数是标点符号,则会返回true |
isspace() | 如果参数是标准的空白字符,如空格、换行符、回车、水平制表符或者是垂直制表符,该函数返回true |
isupper() | 如果参数是大写字母,则返回true |
isxdigit() | 如果参数是十六进制的数字,该函数返回true |
tolower() | 如果参数是大写字母,将会返回他的小写 |
toupper() | 如果参数是小写字母将会返回它的小写 |
1.4 C++的内联函数
编译过程的最终产品是可执行的程序————有一组机器语言组成。程序运行时,操作系统将这些指令载入到计算机中,因此每条指令都有特定的内存地址。计算机随后会逐条执行这些指令。有时(如有循环或者分支语句时),将跳过一些指令,向前或向后跳过一些地址。常规函数调用时也使程序跳到另一个地址,并将在函数结束时返回。下面更详细的介绍着一典型过程。执行到函数调用指令时,程序将在函数调用时立即存储该指令的内存地址,并将函数复制到堆栈中,标记函数起点的内存单元,执行函数代码,然后跳回到地址被保存的指令处(这与阅读文章时停下来看脚注,并在看完脚注时又回到以前阅读的地方类似)。
C++内联函数提供了另一种选择。内联函数的编译代码与其他程序的代码内联起来了。对于内联代码,程序无需跳到另外一个地方去执行代码,再跳回来。因此内联函数运行的速度比常规的稍快,但代价是要占用更多的内存。如果程序在十个不同的地方调用同一个内联函数,在该函数将要包含该函数的十个副本。
另外,当程序员请求将函数作为内联函数时,编译器不一定会满足这样的要求。它可能认为该函数过大或该函数调用了自己(内联函数不能递归),因此不能作为内联函数。下面给一个小例子
<pre class="cpp" name="code"><span style="font-size:18px;">inline double square(double x){return x*x;}
int main()
{
...
a=square(5.0);
b=square(4.5+7.5);
...
}</span>
1.5 创建引用变量
我们要将rodents作为rats的别名,可以这样做:
<span style="font-size:18px;">int rats;
int & rodents=rats;</span>
其中&不是地址运算符,而是类型标示符的一部分。就像生命中的char*指的是指向char的指针一样,int&指的是只想int的引用。上述的声明允许将rats与rodents互换————他们只想相同的内存地址。
对于C语言用户而言,首次接触到引用是可能会有些困惑,会很自然的想到指针,但是他们之间还是有区别的。比如
<span style="font-size:18px;">int rats=101;
int & rodents=rats;
int *prats=&rats;</span>
这样,表达式rodents和*prats都可以与rats互换,而表达式&rodents与prats都可以和&rats互换。从这一点来看,引用很像伪装起来的指针,实际上,引用还是不同于指针的。差别之一是,必须在声明引用时将其初始化,而不能想指针那样先声明再赋值。
1.6 将引用作为函数参数
引用经常被用作函数的参数,是得函数中的变量名称为调用程序中的变量的别名。这种传递参数的方法称为引用传递。按引用传递允许被调用的函数访问调用函数中的变量。C语言只能按值传递,按值传递导致被调用的函数使用调用程序中的值的拷贝。当然,C语言也有避开这种按值传递方式的限制,采用按指针传递的方法。下面我们以交换两个值为例,来对指针和引用做一个对比
<span style="font-size:18px;"><pre class="cpp" name="code">void swapr(int * a,int * b)//use pointers
{
int tmp;
temp=*a;
*a=*b;
*b=temp;
}</span>
<span style="font-size:18px;">void swapp(int *p,int *q)
{
int temp;
temp=*p;
*p=*q;
*q=temp;
}</span>
<span style="font-size:18px;">void swapr(int & a,int & b)//use references
{
int tmp;
temp=a;
a=b;
b=temp;
}</span>
<span style="font-size:18px;"><pre class="cpp" name="code">void swapr(int a,int b)//use pointers{ int tmp; temp= a; a=b; b=temp;}//can't use</span>
如果程序员的意图是让函数使用传递给他的信息而不是修改它,同时又想使用引用,则应该使用常量引用调用。
<span style="font-size:18px;">double refcube(const double &ra);</span>
如果这样做,当编译器发现代码修改ra的值时,将生成错误的信息。
1.7 临时变量、引用参数和const
如果实参与引用参数不匹配,C++将生成临时变量。当前,仅当参数为const时,C++才允许这样做,下面来看看何种情况下C++将生成临时变量,以及何时对const引用的限制是合理的。
首先,什么是创建临时变量呢?如果引用参数为const型,则编译器将在下面两种情况下生成临时变量:
- 实参的类型正确,但是不是左值;
- 实参的类型不正确。
左值是什么?左值参数是可以被引用的数据对象。例如,变量、数组元素、结构成员和解除引用的指针都是左值。非左值包括字面常量和包含多项的表达式。
应尽可能使用const
将引用参数声明为常量数据的引用理由有三个:1.使用const可以避免无意中修改数据的变成错误;2.使用const使函数能够处理const和非const实参,否则将只能接受非const的数据;3.使用const引用是函数能够正确生成和使用临时变量。
这里介绍一下将引用用于结构的情况,先看下如下的代码:
<span style="font-size:18px;"> struct free_throws
{
std::string name;
int made;
int attempts;
float percent;
};
</span>
<span style="font-size:18px;"> void display(const free_throws& ft)
{
using std::cout;
cout<<"Name:"<<ft.name<<endl;
cout<<"Made:"<<ft.made<<endl;
cout<<"Attempts"<<ft.attempts<<endl;
cout<<"Percents"<<ft.percents<<endl;
}
</span><pre class="cpp" name="code"><span style="font-size:18px;">free_throws& accumulate(free_throws& target,const free_throws& source)
</span>
{ target.attempts +=source.attempts; target.made +=source.made; return target; }
有关结构体的相关介绍我就不再这里累赘了。下面我们来看看这条语句
<span style="font-size:18px;">free_throws& accumulate(free_throws& target,const free_throws& source)
</span>
如果返回类型被声明为free_throws而不是free_throws&,上述返回语句将返回target的拷贝。但返回的类型为引用,这意味着返回的是最初传递给函数的结构体对象。比如
<span style="font-size:18px;">dup=accumulate(team,five);</span>
如果accumulate()返回的是一个结构,而不是指向结构的引用,将把整个结构复制到一个临时的位置,再将这个拷贝复制给dup。担当返回值为引用时,将直接把team复制给dup,其效率更高。
不过在返回引用时也需要注意一些问题,应避免返回数据终止时不再存在的内存单位引用。您因避免有如下的代码
<span style="font-size:18px;">const free_throws& clone2(free_throws& ft)
{
free_throws newguy;
newguy=ft;
return newguy;
}</span>
该函数返回一个指向临时变量的引用,函数执行完毕后他将不再存在。为避免这种问题最简单的方法是返回一个作为参数传递给函数的引用。作为参数的引用将指向调用函数使用的数据,因此返回的引用也将指向这些数据。另一种方法是用new来分配新的存储空间,但是当我们不再需要这个结构时应该使用delete将其释放。