《21天学通C++(第五版)》学习笔记 [美] Jesse Liberty Bradley Jones著

第一部分  绪论

1.程序:(1)指程序员编写的源代码;(2)指可执行软件。

2.过程化编程:过程(函数或方法)是指一组依次执行的指令,数据与过程分离,编程技巧是跟踪函数的调用方向和函数对数据的修改动向。

3.结构化编程:其主要思想是分而治之,即是将过于复杂、无法简单解决的任务分解为不可再分的小任务,从而完成编程。

4.面向对象编程(OOP):其实质是模拟对象(东西或概念)而不是数据,从而实现将数据和操作结合起来。

5.C++支持的面向对象编程包括三要素:封装、继承和多态。

5.1封装:实现对数据和属性的隐藏,成为自包容单元的特性称为封装,C++中,利用“类(class)”来实现。

5.2继承:从已有类继承一部分属性成为新类,这类新类被称为派生类。

5.3多态:指同一名称有多种形式。

6.ANSI标准:隶属于美国国家标准协会的授权委员会(Accredited Standards Cpmmittee)制定了C++国际标准,该标准也被称为ISO(国际标准化组织)标准、NCITS(国家信息技术标准化委员会)标准、X3(NCITS的前身)标准和ANSI/ISO标准。

7.创建可执行文件的步骤是:1、创建扩展名为.cpp的源代码;2、将源代码文件编译成扩展名为.obj或.o的目标文件;3、将目标文件与所需的各种库链接起来,生成可执行程序。

程序清单1.1 HELLO.CPP

1: #include<iostream>

2:int mian()

3:{

4:    std::cout <<"Hello Wrold!\n";

5:    return 0;

6:}


第二部分  C++程序的组成部分

1.mian函数的返回值被设置void是不合法的,习惯性让main函数返回int。

程序清单 2.1 使用cout

1://Listing 2.2 using std::cout
2:#include<iosteram>
3:int main()
4:{
5:    std::cout<<"Hallo there.\n";
6:    std::cout<<"Here is 5:"<<5<<"\n";
7:    std::cout<<"The manipulator std::endl";
8:    std::cout<<"writes a new line to the screen.";
9:    std::cout<<std::endl;
10:   std::cout<<"Here is a very big number:\t"<<70000;
11:   std::cout<<std::endl;
12:   std::cout<<"Here is the sum of 8 and 5:\t";
13:   std::cout<<8+5<<std::endl;
14:   std::cout<<"Here's a fraction:\t\t";
15:   std::cout<<(float)5/8<<std:endl;
16:   std::cout<<"And a very very big number:\t";
17:   std::cout<<(double)7000*7000<<std::endl;
18:   std::cout<<"Don't forget to replace Jesse Liberty";
19:   std::cout<<"with your name...\n";
20:   std::cout<<"Jesse Liberty is a C++ programmer!\n";
21:   return 0;
22:} 

注:endl是指end line。

程序清单2.2 使用关键字using

1://Listing 2.3 - using the using keyword
2:#include<iostream>
3:int main()
4:{
5:      using std::cout;
6:    using std::endl;
7:
8:    cout<<"Hello there.\n";
9:   cout<<"Here is 5:"<<5<<"\n";
10:    cout<<endl;
11:}
注:这里的using是提前告诉编译器将使用标准库的两个对象的语句,所以就不需要限定cout和endl了。另一个用法就是using namespace std;,但是使用using namespace的缺点是可能不小心使用了错误库中的对象。所以较为推荐使用第一种做法。

2.注释的类型:1、单行注释;2、多行注释。其中单行注释使用双斜杠(//)来表示,即双斜杠告诉编译器,忽略之后到行尾的所有内容。而多行注释则是以单斜杠和星(/*)来打头的。这中注释是告诉编译器,忽略之后到星和单斜杠(*/)之间的所有内容,这两种注释标记可以位于同一行,之间也可以有一行或多行,但是每个/*必须要有*/来配对。

3.注释的使用:1、在函数的开头使用,说明函数的功能和返回值;2、在晦涩难懂的地方,加入注释以帮助理解。3、注释应该说明为什么会发生这样的事情,而不是说明发生了什么。

4.函数(简介):系统调用,当程序中没有函数时,程序依然可以运行,当程序遇到函数时,将会执行函数,当函数执行完后,将会继续执行下一条语句。

程序清单2.3 演示函数调用

#include <iostream>

//function Demonstration Function
//prints out a useful message
void DemonstrationFunction()
{
    std::cout<<"In Demonstration Function\n";
}

//function main - prints out a message,then
//calls DemonstrationFunction,then prints out
//a second message

int main()
{
    std::cout<<"In main\n";
    DemonstrationFunction();
    std::cout<<"Back in main\n";
    return 0;
}
5.函数的使用:函数由函数头和函数体组成,而函数头又由返回类型、函数名和参数组成,函数参数让你能够将值传递给函数。参数用于声明要传入的值的类型(调用函数实际传入的值被称为实参,而在函数头上标明的参数为形参)函数主体由左大括号、零条或者更多的语句以及右大括号组成,函数的功能由语句实现。函数可能用return语句来返回一个值,如果函数不包括return语句则函数将在自动返回void。

程序清单2.4 函数返回值

#include <iostream>
int Add(int first,int second)
{
    std::cout<<"In Add(),received"<<first<<"and"<<second<<"\n";
    return (first+second);
}

int main()
{
    using std::cout;
    using std::cin;
    using std::endl;

    cout<<"I'm in main()!"<<endl;
    int a,b,c;
    cout<<"Enter two numbers:";
    cin>>a>>b;
    cout<<endl<<"Calling Add()"<<endl;
    c=Add(a,b);
    cout<<endl<<"Black in main().\n";
    cout<<"C was set to "<<c;
    cout<<"\nExiting...\n\n";
    return 0;
}


第三部分  使用变量和常量

1.变量:指存储信息的空间,变量是计算机内存的一个位置,可以在其中存储值或检索其中的值,其变量用于临时存储,退出程序或关机后,变量中的信息将丢失。

2.变量的大小:任何变量在内存中都将占据一定空间,而对任何机器来说,其占用空间的大小是固定的。单个字符用char变量存储(大小通常为一个字节),而整型变量则用int来存储(可能2字节,也可能是4字节),对于较小的整数,则用short变量来存储(大多数机器上,short通常为2字节,而long通常为4字节)。C++规定,short的长度不可以超过int,而int不可以超过long。

程序清单3.1

#include <iostream>

int main()
{
    using namespace std;

    cout<<"The size of an int is:\t\t"<<sizeof(int)<<"bytes.\n";
    cout<<"The size of a short is:\t\t"<<sizeof(short)<<"bytes.\n";
    cout<<"The size of a long is:\t\t"<<sizeof(long)<<"bytes.\n";
    cout<<"The size of a char is:\t\t"<<sizeof(char)<<"bytes.\n";
    cout<<"The size of a float is:\t\t"<<sizeof(float)<<"bytes.\n";
    cout<<"The size of a double is:\t\t"<<sizeof(double)<<"bytes.\n";
    cout<<"The size of a bool is:\t\t"<<sizeof(bool)<<"bytes.\n";

    return 0;
}
注:sizeof可以求出给定类型的长度。

3.signed和unsigned:其中signed可以让变量带有正负号,而unsigned则只可以让变量为正。

4.基本变量类型:C++内置浮点变量和字符变量。其中浮点变量为实数,字符变量为字符(通常为1位),通常用来表示ASCII字符集和扩展ASCII字符集中的256个字母和符号。

5.定义变量:要创建或定义变量,可声明其类型,然后指出变量名称,再加分号结尾。变量名可为任意字母组合,但不能有空格,不能以数字开头,也不可使用C++预定义的符号名。同时要注意,C++是区分大小写的。比较流行的命名方法是:1、my_cat、2、myCat(第二种被叫做驼峰法)、3、匈牙利表示法:即变量名以一组表示其类型的字符打头,例如指针就用p打头。但是注意:匈牙利表示法不推荐使用。

6.C++系统关键字

   ISO C++98/03关键字共63个,此处严格按标准原文排版:

asm

do

if

return

typedef

auto

double

inline

short

typeid

bool

dynamic_cast

int

signed

typename

break

else

long

sizeof

union

case

enum

mutable

static

unsigned

catch

explicit

namespace

static_cast

using

char

export

new

struct

virtual

class

extern

operator

switch

void

const

false

private

template

volatile

const_cast

float

protected

this

wchar_t

continue

for

public

throw

while

default

friend

register

true


  

delete

goto

reinterpret_cast

try

 

 


 

C++11 关键字共 73个。
新增关键字:alignas、alignof、char16_t、char32_t、constexpr、decltype、noexcept、nullptr、static_assert、thread_local。

其中auto意义改变为:表示由编译器静态判断其应有的类型;register 被视为过时;export 因为实现支持太少(仅Edison Design Group的前端支持),编译效率低下,取消原有意义(仍是关键字,但使用它的程序是错误的),改为保留给未来标准使用。

7.同时创建多个变量:在创建变量时,用逗号将变量名分开即可。

8.给变量赋值:使用赋值运算符(=)给变量赋值。

程序清单:3.2 演示变量的用法

#include <iostream>
int main()
{
    using std::cout;
    using std::endl;

    unsigned short Width = 5,Length;
    Length = 10;

    //create an unsigned short and initialize with result
    //of multiplying Width by Length
    unsigned short Area = (Width * Length);
    cout<<"Width:"<<Width<<endl
        <<"Length:"<<Length<<endl
        <<"Area:"<<Area<<endl;

    return 0;
}
注:long是long int的缩写,short是short int的缩写。

9.用typedef来创建别名:C++允许使用关键字typedef(表示类型定义)来给一个短语创建一个别名。例:typedef int INT;

10.变量类型使用原则:当需要存储的数值足够大时,应将变量声明为更大类型的变量。

11.unsigned和singned:这两个关键字分别定义了无符号类型和有符号类型的变量。

12.字符变量(char):通常占一个字节,char变量通常被解释为0~256的数或ASCII码字符集中的成员,其中ASCII码97表示小写字母a,ASCII码65表示大写字母A,ASCII码48表示数字0。

程序清单3.3 利用数字打印字符

#include <iostream>
int main()
{
    for(int i =0;i<128;i++)
        std::cout<<(char)i;
    return 0;
}
13.特殊打印字符:一些常用的特殊格式化字符,用反斜杠(/,转义字符)和相应的字符输入。

常用的转义字符表:

转义字符

意义

ASCII码值(十进制)

\a

响铃(BEL)

007

\b

退格(BS) ,将当前位置移到前一列

008

\f

换页(FF),将当前位置移到下页开头

012

\n

换行(LF) ,将当前位置移到下一行开头

010

\r

回车(CR) ,将当前位置移到本行开头

013

\t

水平制表(HT) (跳到下一个TAB位置)

009

\v

垂直制表(VT)

011

\\

代表一个反斜线字符''\'

092

\'

代表一个单引号(撇号)字符

039

\"

代表一个双引号字符

034

\?
  

代表一个问号
  

063
  

\0

空字符(NULL)

000

\ddd

1到3位八进制数所代表的任意字符

三位八进制

\xhh

1到2位十六进制所代表的任意字符

二位十六进制

注意:区分,斜杠:"/" 与 反斜杠:"\" ,此处不可互换

14.常量:不能修改的值,创建常量时,必须要赋值,且在初始化之后就无法赋值了。C++有两种常量:字面常量和符号常量。

14.1.字面常量:指直接输入到程序中的值,例如:int a = 19; 其中19就是字面常量

14.2.符号常量:指用名称表示的常量,传统方法定义常量是用#define,现在使用的是关键字const来创建。例如const int a =10;

15.枚举常量:可利用枚举常量创建新的类型,并定义新类型变量,同时将这些变量的取值限定为一定范围的可能值,创建枚举常量的方法是:关键字enum,新类型名,左大括号,合法值(用逗号隔开)右大括号。每个枚举常量都有一个整数值,如不特殊指定,则第一个常量的值为0,其余常量的值依次递增。

程序清单3.4

#include <iostream>
int main()
{
    enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};
    Days Today;
    Today = Monday;
    const int a = 3;
    if (a == Today || Today == Saturday)
        std::cout<<"\nGotta'love the weekends!\n";
    else
        std::cout<<"\nBack to work.\n";
    return 0;
}

第四部分  创建表达式和语句

1.语句:在C++中,语句控制程序的执行顺序、计算表达式的值或者什么都不做(空语句)。所有C++语句都已分号(;)结尾。

2.语句块和复合语句:在任何可以使用单条语句的地方都可以使用复合语句(也叫语句块),语句块以“{”开始,以“}”结尾。注意:语句块不需要用”;“结尾。

3.表达式:表达式总是返回一个值,而当表达式带上分号后,就称为语句。

4.运算符:指能让编译器执行某种操作的符号,常用于操作数,分为多种,先学习两种:赋值运算符和数学运算符。

4.1.赋值运算符(=):将表达式左边的值赋给右边表达式的值。注:放在赋值运算符左侧的操作数叫左值,放在右侧的操作数叫右值,所有的左值都是右值,但所有的右值并非都是左值。其中,常量是右值,因为他们不可以被修改,所以常量不能是左值。

4.2数学运算符:有五个,分别是+、-、*、/和%(求模)。求模也叫取余,即求得余数。

5.赋值运算符和求值运算符结合:+=、-=、*=、/=、%=等都是赋值运算符和求值运算符的结合。例:a+=3 <=> a = a+3;

6.自增和自减运算符:++为自增运算符,--为自减运算符。这两种运算符有前缀和后缀之分:其中前缀时,先修改变量的值,再求表达式的值。例: i = 1;++i; 后,i的值为2,表达式的值也为2。而后缀时,先求表达式的值,再修改变量的值。例:i = 1; i++;后,表达式的值为2,而i的值任然为1。

程序清单4.1 前缀和后缀表达式的使用

#include <iostream>

int main()
{
    using std::cout;

    int myAge = 39;
    int yourAge = 39;
    cout<<"I am:"<<myAge<<"years old\n";
    cout<<"You are:"<<yourAge<<"years old\n";

    myAge++;
    ++yourAge;
    cout<<"One year passes...\n";
    cout<<"I am:"<<myAge<<"years old\n";
    cout<<"You are:"<<yourAge<<"years old\n";
    cout<<"Another year passes\n";
    cout<<"I am:"<<myAge++<<"years old\n";
    cout<<"You are:"<<++yourAge<<"years old\n";
    cout<<"Let's print it again.\n";
    cout<<"I am:"<<myAge++<<"years old\n";
    cout<<"You are:"<<yourAge<<"years old\n";

    return 0;
}

7.算数运算符的优先级

C++中的算数运算符的优先级为:

Operator(运算符)
Description(描述)
Example(例子)
Overloadable(替换)
Group 1(no associativity)
     
::
Scope resolution operator
Class::age = 2;
NO
Group 2
     
()
Function call
isdigit('1')
YES
()
Member initalization
c_tor(int x, int y) : _x(x), _y(y*10){};
YES
[]
Array access
array[4] = 2;
YES
->
Member access from a pointer
ptr->age = 34;
YES
.
Member access from an object
obj.age = 34;
NO
++
Post-increment
for( int i = 0; i < 10; i++ ) cout << i;
YES
--
Post-decrement
for( int i = 10; i > 0; i-- ) cout << i;
YES
const_cast
Special cast
const_cast<type_to>(type_from);
NO
dynamic_cast
Special cast
dynamic_cast<type_to>(type_from);
NO
static_cast
Special cast
static_cast<type_to>(type_from);
NO
reinterpret_cast
Special cast
reinterpret_cast<type_to>(type_from);
NO
typeid
Runtime type information
cout &laquo; typeid(var).name();
cout &laquo; typeid(type).name();
NO
Group 3(right-to-left associativity)
     
!
Logical negation
if( !done ) …
YES
not
Alternate spelling for !
   
~
Bitwise complement
flags = ~flags;
YES
compl
Alternate spelling for ~
   
++
Pre-increment
for( i = 0; i < 10; ++i ) cout << i;
YES
--
Pre-decrement
for( i = 10; i > 0; --i ) cout << i;
YES
-
Unary minus
int i = -1;
YES
+
Unary plus
int i = +1;
YES
*
Dereference
int data = *intPtr;
YES
&
Address of
int *intPtr = &data;
YES
new
Dynamic memory allocation
long *pVar = new long;
MyClass *ptr = new MyClass(args);
YES
new []
Dynamic memory allocation of array
long *array = new long[n];
YES
delete
Deallocating the memory
delete pVar;
YES
delete []
Deallocating the memory of array
delete [] array;
YES
(type)
Cast to a given type
int i = (int) floatNum;
YES
sizeof
Return size of an object or type
int size = sizeof floatNum;
int size = sizeof(float);
NO
Group 4
     
->*
Member pointer selector
ptr->*var = 24;
YES
.*
Member object selector
obj.*var = 24;
NO
Group 5
     
*
Multiplication
int i = 2 * 4;
YES
/
Division
float f = 10.0 / 3.0;
YES
%
Modulus
int rem = 4 % 3;
YES
Group 6
     
+
Addition
int i = 2 + 3;
YES
-
Subtraction
int i = 5 - 1;
YES
Group 7
     
<<
Bitwise shift left
int flags = 33 << 1;
YES
>>
Bitwise shift right
int flags = 33 >> 1;
YES
Group 8
     
<
Comparison less-than
if( i < 42 ) …
YES
<=
Comparison less-than-or-equal-to
if( i <= 42 ) ...
YES
>
Comparison greater-than
if( i > 42 ) …
YES
>=
Comparison greater-than-or-equal-to
if( i >= 42 ) ...
YES
Group 9
     
==
Comparison equal-to
if( i == 42 ) ...
YES
eq
Alternate spelling for ==
   
!=
Comparison not-equal-to
if( i != 42 ) …
YES
not_eq
Alternate spelling for !=
   
Group 10
     
&
Bitwise AND
flags = flags & 42;
YES
bitand
Alternate spelling for &
   
Group 11
     
^
Bitwise exclusive OR (XOR)
flags = flags ^ 42;
YES
xor
Alternate spelling for ^
   
Group 12
     
|
Bitwise inclusive (normal) OR
flags = flags | 42;
YES
bitor
Alternate spelling for |
   
Group 13
     
&&
Logical AND
if( conditionA && conditionB ) …
YES
and
Alternate spelling for &&
   
Group 14
     
||
Logical OR
if( conditionA || conditionB ) ...
YES
or
Alternate spelling for ||
   
Group 15(right-to-left associativity)
     
? :
Ternary conditional (if-then-else)
int i = (a > b) ? a : b;
NO
Group 16(right-to-left associativity)
     
=
Assignment operator
int a = b;
YES
+=
Increment and assign
a += 3;
YES
-=
Decrement and assign
b -= 4;
YES
*=
Multiply and assign
a *= 5;
YES
/=
Divide and assign
a /= 2;
YES
%=
Modulo and assign
a %= 3;
YES
&=
Bitwise AND and assign
flags &= new_flags;
YES
and_eq
Alternate spelling for &=
   
^=
Bitwise exclusive or (XOR) and assign
flags ^= new_flags;
YES
xor_eq
Alternate spelling for ^=
   
|=
Bitwise normal OR and assign
flags |= new_flags;
YES
or_eq
Alternate spelling for |=
   
<<=
Bitwise shift left and assign
flags <<= 2;
YES
>>=
Bitwise shift right and assign
flags >>= 2;
YES
Group 17
     
throw
throw exception
throw EClass(“Message”);
NO
Group 18
     
,
Sequential evaluation operator
for( i = 0, j = 0; i < 10; i++, j++ ) …
YES

8.括号的嵌套:为了改变运算的顺序,可利用括号来改变。但不可以嵌套过深,运算顺序为:现括号里的再括号外的。

9.真值的本质:表达式都有值,可以将每个表达式的值视为真或假,如果表达式的值为零,则返回false,否则返回true。再新的ANSI中引入了bool型,这种类型只可以取两种值:false和ture。

10.关系运算符:C++中的6个关系运算符分别是:==(等于)、<(小于)、>(大于)、<=(小于等于)、>=(大于等于)、!=(不等)。有这些运算符组成的表达式只会返回ture和false。

11.if语句:测试某个条件是否成立,如果成立则执行语句,反之则不执行。

程序清单4.2 基于关系运算符进行分支

#include <iostream>

int main()
{
    using std::cout;
    using std::cin;
    using std::endl;

    int MetScore,YankeeScore;
    cout<<"Enter the score for the Mets:";
    cin>>MetScore;

    cout<<endl<<"Enter the score for the Yankee:";
    cin>>YankeeScore;

    cout<<endl;

    if(MetScore > YankeeScore)
        cout<<"Let's Go Mets!"<<endl;
    if(MetScore < YankeeScore)
    {
        cout<<"Go Yankees!"<<endl;
    }
    if(MetScore == YankeeScore)
    {
        cout<<"A tie?Naah,can't be."<<endl;
        cout<<"Give me the real score for the Yanks:";
        cin>>YankeeScore;
        if(MetScore > YankeeScore)
            cout<<"Knew it!Go Yanks!";
        if(YankeeScore > MetScore)
            cout<<"Knew it!GO Yanks!";
        if(YankeeScore == MetScore)
            cout<<"Wow,it really was a tie!";
    }
    cout<<endl<<"Thanks for telling me."<<endl;

    return 0;
}
注:在if判断条件后不需要加分号(;),加入分号则代表执行一条空语句。所以为了减少错误,请在if后使用大括号。

12.缩进风格:大括号的对齐风格现在比较流行的缩进风格有三种:

(1)if(expression){

                   statements

         }

(2)if(expression)

        {

                   statements

        }

(3)if(expression)

            {

                   statements

           }

C++程序员比较常用是第二种风格。

13.else语句:程序常常在if判断后不执行某个分支,而需要执行另外一分支,所以可以使用关键字else。

     if(expression)

            {

                   statements

           }

   else

          {

                   statements

           }

 14.if和else语句的嵌套使用:多个分支均则可以使用if和else组合

程序清单4.3

#include <iostream>
int main()
{
    using namespace std;

    int firstNumber,secondNumber;
    cout<<"Enter two numbers."<<endl<<"First:";
    cin>>firstNumber;
    cout<<endl<<"Second:";
    cin>>secondNumber;
    cout<<endl<<endl;

    if(firstNumber >= secondNumber)
    {
        if((firstNumber & secondNumber) == 0)
        {
            if(firstNumber == secondNumber)
                cout<<"They are the same!"<<endl;
            else
                cout<<"They are evenly divisible!"<<endl;
        }
        else
            cout<<"They are not evenly divisible!"<<endl;
    }
    else
        cout<<"Hey!The second not is larger!"<<endl;

    return 0;
}
15.逻辑运算符:&&(AND)、||(OR)、!(NOT)。AND运算符:运算符两侧的运算数同时为真时,表达式的结果才会为真。OR运算符:运算符两侧的运算数同时为假时,表达式的结果才会为假,NOT运算符:使表达式的值与表达式的值相反。

16.0的使用:在C++中,0被解释为假,其他非0数都被解释为真。所以在如下例子中,推荐使用第二种。

(1)if(x = 0)

             x = 0;

(2)if(x !=  0)

           x = 0;

注:良好的编程习惯就是:用表达式来判断逻辑真假,而不使用它本身来判断逻辑真假。

16.三目运算符:条件运算符(?:)是C++\中唯一一个三目运算符,即它是唯一一个需要三个操作数的运算符。条件运算符接受单个表达式并返回一个值:(experssion1)?(expression2):(expression3)这个表达式的含义是:如果expression1为真,则返回expression2的值,否则返回expression3的值。

程序清单4.4

#include <iostream>
int main()
{
    int a,b,c;
    std::cin>>a>>b;
    c = (a > b)? a : b;
    std::cout<<c<<std::endl;
    return 0;
}
第五部分 组织函数

1.函数:指能够对数据进行处理并返回一个值的子程序,每个C++程序必须要有一个函数:main函数。每个函数都有自己的函数名,当程序遇到函数名时候,会执行函数(调用函数),当函数执行完时(遇到return语句或左大括号“}”),程序返回到函数调用的下一行继续执行。函数主要有两种类型:1、用户定义函数,2、内置函数。其中内置函数是由开发商提供给用户的。

2.返回值、参数和实参:函数可以接受值,还可以返回一个值。调用函数完成工作,返回一个值或者工作结果。这个值被称为返回值,返回值的类型必须被声明。另外,参数描述了函数被调用时,传递给它的值的类型,传递给函数的实际值被称为实参。

3.声明和定义函数:在程序中使用函数,需要先声明函数后在使用。声明将函数的名称、返回值和参数告诉编译器;定义将函数的工作原理告诉编译器。函数的声明又被称为原型。有3中声明函数的方法:1、将函数原型放在文件中,然后使用#include将该文件包含在程序内;2、将函数原型放在其中使用它的文件中;3、在函数被其他函数调用前定义它(这样做,函数定义将作为原型)。

4.函数原型:指一条语句,以分号结尾,由函数的返回值类型和特征标(signature)组成,函数特征标包含函数名和参数列表,参数列表是所有参数及其类型的列表,参数之间用逗号分开。注:函数原型及其定义再返回类型和特征标方面必须完全相同,否则编译时会出错。同时,函数原型可以不包含参数名,而只有参数类型。同时,所有函数都必须要有返回值,如果没有明确声明,则默认为int。如果函数不需要返回值,则可以将函数返回类型设置为void。

程序清单5.1 函数的声明、定义和用法

#include<iostream>

int Area(int,int);

int main()
{
    using namespace std;

    int lengthofYard;
    int widthofYard;
    int areaofYard;

    cout<<endl<<"How wide is your yard?";
    cin>>widthofYard;
    cout<<endl<<"How long is your yard?";
    cin>>lengthofYard;

    areaofYard = Area(lengthofYard,widthofYard);

    cout<<endl<<"Your yard is:"
        <<areaofYard
        <<"square feet"<<endl<<endl;

    return 0;
}

int Area(int len,int wid)
{
    return len * wid;
}

5.变量的作用域:每个变量都有作用域,作用域决定了变量在程序中的存活时间以及在什么地方可以使用它。其中,在语句块中声明的变量的作用域为该语句块;全局变量则在程序任何地方都可以使用它。

6.局部变量:在函数体内声明的变量被称为局部变量,当函数返回时,这些变量将不再存在,编译器将其标记并销毁。另:传入的参数也可看作局部变量。

程序清单5.2 局部变量和参数的用法

#include <iostream>
float Convert(float);

int main()
{
    using namespace std;

    float TempFer;
    float TempCel;

    cout<<"Please enter the temperature in Fahrenheit:";
    cin>>TempFer;
    TempCel = Convert(TempFer);
    cout<<endl<<"Here's the temperature in Celsius:";
    cout<<TempCel<<endl;

    return 0;
}

float Convert(float TempFer)
{
    float TempCel;
    TempCel = ((TempFer - 32) * 5)/9;
    return TempCel;
}
7.作用域为语句块的局部变量:在函数的任何地方定义变量,且不仅限于函数开头,变量的作用域为定义它的语句所在的语句块。

8.参数作为局部变量:传入函数的参数为函数的局部变量,修改这些参数不会影响调用函数中的值,这被称为按值传递,也就是在函数中会创建参数的局部拷贝。

程序清单 5.3

#include <iostream>
using namespace std;
void swap(int,int);

int main()
{
    int x = 5,y = 10;

    cout<<"Main.Before swap,x:"<<x<<"\ty"<<y<<endl;
    swap(x,y);
    cout<<"Main.Before swap,x:"<<x<<"\ty"<<y<<endl;

    return 0;
}

void swap(int x,int y)
{
    int temp;

    cout<<"Swap.Before swap,x:"<<x<<"\ty:"<<y<<endl;

    temp = x;
    x = y;
    y = temp;

    cout<<"Swap.Before swap,x:"<<x<<"\ty:"<<y<<endl;
}
9.全部变量:在函数外面定义的变量的作用域为全局,在程序的任何地方都可以使用。与全局变量同名的局部变量不会修改全局变量,但会隐藏全局变量,即在函数中有一个与全局变量同名的局部变量,则在函数中使用该名称时,指的是局部变量而不是全局变量。注:全局变量在C++中很少使用,因为全局变量可以被修改,导致不可预计的错误,因此常用静态成员变量替代全局变量。

10.再谈函数实参:任意合法的C++表达式中都可以作为函数实参,包括常量、数字和逻辑表达式以及返回的一个值的函数,需要注意的是表达式的结果必须与函数期望的实参类型匹配。

11.再谈返回值:返回值可以返回一个值或者不返回值,也可以返回逻辑真值,并且在遇到关键字return时,其后的表达式将作为函数的返回值,并立即返回到调用的喊出,return后的所有语句都不会被执行。

程序清单 5.4 包含多条返回的语句

#include <iostream>

int Doubler(int);

int main()
{
    using std::cout;
    using std::endl;
    int input;
    int result = 0;

    cout<<"Enter a number between 0 and 10,000 to double:";
    std::cin>>input;

    cout<<endl<<"Before double is called...";
    cout<<endl<<"input:"<<input<<"doubled:"<<result<<endl;

    result = Doubler(input);

    cout<<endl<<"Back from called..."<<endl;
    cout<<endl<<"input:"<<input<<"doubled:"<<result<<endl;

    return 0;
}

int Doubler(int original)
{
    if(original <= 10000)
        return original * 2;
    else
        return -1;
    std::cout<<"You can't get here!"<<std::endl;
}
注:int main()和void main()两者都被大多数编译器支持,但是就ANSI标准来说,只有int main()符合ANSI标准。

12.,默认参数:每个函数必须接受参数,当不传递参数时,会引起错误。但是可以有例外就是使用默认参数。即在函数参数中给参数定义一个预定义的值,例如:int Area (int  a = 10);这个时候,当没有参数传入时,编译器将把x设置为默认值10。注:可以给任何函数参数指定默认值,但是如果某个参数没有设置默认值时,他前面的所有参数都不可以有默认值。

程序清单 5.5 默认参数值

#include <iostream>

int AreaCube(int length,int width = 25,int height = 1);

int main()
{
    int length = 100;
    int width = 50;
    int height = 2;
    int area;

    area = AreaCube(length,width,height);
    std::cout<<"First area equals:"<<area<<std::endl;

    area = AreaCube(length,width);
    std::cout<<"First area equals:"<<area<<std::endl;

    area = AreaCube(length);
    std::cout<<"First area equals:"<<area<<std::endl;

    return 0;
}

AreaCube(int length,int width,int height)
{
    return (length * width * height);
}
注:如果第二个参数没有默认值的时,不要为第一个参数设置默认值,按值传递参数不会修改函数中的变量。

13.重载函数:C++允许创建多个名称相同的函数,该函数被称为函数重载。在这些同名的函数中的参数列表中,必须有不同的参数类型、参数个数或兼而有之。注:如果两个函数的函数名和参数列表相同,但返回类型不同,将导致编译错误,要修改返回类型,必须同时修改特征标(名次和/或参数列表)。

14.函数多态:函数重载也叫函数多态(polymorphism)。多态指的是多种形态。即可以对函数进行重载,使之含有多种含义。通过修改参数的个数或类型,可以让多个函数使用相同的名称。进而根据指定的参数,调用与之匹配的函数。

函数清单 5.6 函数多态

#include <iostream>

int Double(int);
long Double(long);
float Double(float);
double Double(double);

using namespace std;

int main()
{
    int myInt = 6500;
    long myLong = 6500;
    float myFloat = 6.5F;
    double myDouble = 6.5e20;

    int doubleInt;
    long doubleLong;
    float doubleFloat;
    double doubleDouble;

    cout<<"myInt:"<<myInt<<endl;
    cout<<"myLong:"<<myLong<<endl;
    cout<<"myFloat:"<<myFloat<<endl;
    cout<<"myDouble:"<<myDouble<<endl;

    doubleInt = Double(myInt);
    doubleLong = Double(myLong);
    doubleFloat = Double(myFloat);
    doubleDouble = Double(myDouble);

    cout<<"myInt:"<<doubleInt<<endl;
    cout<<"myLong:"<<doubleLong<<endl;
    cout<<"myFloat:"<<doubleFloat<<endl;
    cout<<"myDouble:"<<doubleDouble<<endl;

    return 0;
}

int Double(int original)
{
    cout<<"In Double(int)"<<endl;
    return 2 * original;
}


long Double(long original)
{
    cout<<"In Double(long)"<<endl;
    return 2 * original;
}


float Double(float original)
{
    cout<<"In Double(float)"<<endl;
    return 2 * original;
}

double Double(double original)
{
    cout<<"In Double(double)"<<endl;
    return 2 * original;
}
15.内联函数:当函数非常小,但是有需要频繁调用时,为了提高效率,可使用内联函数。内联函数是在声明函数时候,使用关键字inline,让编译器不会创建函数,而直接将内联函数的代码复制到调用函数中,以减少不必要的开销。注:为了避免因为内联函数过大,在编译器复制后,导致可执行程序变大,而降低速度。故推荐当函数只有一两句话时,再使用内联函数。
16.递归函数:函数可以调用自身,这被称为递归,递归可以是直接的或间接的。直接递归指函数调用自身;而间接递归是指函数调用另外一个函数,而后者又调研难过了它。注:函数调用会创建函数的新拷贝空间,因此当递归次数多大时,会造成很大的空间开销。并且递归函数需要一个结束条件,必须要有某个条件导致程序停止递归。

程序清单 5.7 使用Fibonacci数列演示递归

#include <iostream>

int fib(int);

int main()
{
    int n,answer;
    std::cout<<"Enter number to find:";
    std::cin>>n;

    std::cout<<std::endl<<std::endl;

    answer = fib(n);

    std::cout<<answer<<" is the "<<n;
    std::cout<<"th Fibonacci number"<<std::endl;

    return 0;
}

int fib(int n)
{
    std::cout<<"Processing fib("<<n<<")...";

    if(n<3)
    {
        std::cout<<"Return 1!"<<std::endl;
        return (1);
    }
    else
    {
        std::cout<<"Call fib("<<n - 2<<")";
        std::cout<<"and fib("<<n - 1<<")."<<std::endl;

        return (fib(n - 2) + fib(n - 1));
    }
}
17.内存分区:C++里会把内存分为全局名称区、自由存储区、寄存器、代码空间和堆栈。其中全局名称区是存放全局变量的。寄存器是在CPU中的特殊存储区域,可将寄存器统称为指令指针,代码空间是存放代码的地方,堆栈是用来存放程序中每个函数所需的数据。堆栈是先进后出。

18.堆栈与函数:程序执行过程中发生的大致情况(具体细节随操作系统和编译器差别而差别)。

       (1)指令指针中的地址增加1,指向函数调用后的下一条指令,这个地址随后被放入堆栈,它是函数返回时的返回地址;

       (2)在堆栈中为声明的返回值类型分配空间,在int变量占两个字节的系统上,如果返回类型被声明为int,堆栈再增加两个字节,但这两个字节中不存放任何值(这就意味着这两个值中的数据(垃圾数据)保持不变,直到本地变量被初始化);

      (3)被调用函数的地址存储在为此而分配的一块特殊区域中,这个地址被加载到指令指针中,这样将执行的下一条指令为被调用的函数;

      (4)当前的栈顶被记录下来,并存入一个称为栈帧(stack frame)的特殊指针中,从现在开始到函数返回被加入到堆栈中任何数据都将被视为函数的局部数据;

      (5)函数的所有参数都被放入堆栈;

      (6)现在执行指令指针中的指令,即执行函数的第一条指令;

      (7)局部变量在被定义时被压入堆栈。

       当函数准备好返回时,返回值被放入第2步预留的堆栈区域中,随后不断对堆栈执行弹出操作,直接遇到栈帧指针,这相当于丢弃函数的所有局部变量和参数。

       返回值被弹出堆栈,将其作为函数调用本身的值,然后检索第1步存储的地址,将其放入指令指针。程序回到函数调用后执行,并检索函数调用的返回值。

第六部分 面向对象编程

1.面向对象编程:将数据和操作数据的方法结合在一起,从而更加容易理解。面向对象是C++和C之间的桥梁。

2.使用结构创建新类型的缺点:通过将相关变量组合成结构的方法,为C语言增加了新类型的功能,通过使用typedef可以让结构称为新的类型。但是缺点有:1、结构和操作不是一个整体,只能通过阅读库的头文件,并使用新类型作为参数进行查找,才能找到函数。2、针对结构的相关函数组的行为进行协调时,任何数据都有可能在任何时候被修改,无法防止数结构数据被修改不被干扰,从而造成错误。3、内置的运算符不适用于结构:不能使用(+)将两个结构变量相加,即使这可能是一种最自然的表示问题解决方案的方式。

3.类:在C++中,可通过声明一个类来创建一个新类型,类将一组变量和一组相关操作函数组合在一起。类可以将一个事物的数据和属性以及相关操作封装在一个集合中,这个集合就被称为对象。成员变量也叫数据成员,类内的函数被称为成员函数或者类函数。类函数通常操作成员变量。

4.声明类:使用关键字class,后跟类名,左大括号,数据成员列表和方法,右大括号和分号。例:class Cat { unsigned itsAge; unsigned itsWeight; void Meow();};注:上述声明并没有分配内存,只是告诉编译器Cat的相关属性和数据,但是声明让编译器知道Cat多大,即编译器必须为你创建的每个Cat对象预留多少内存。

5.程序命名:软件开发公司通常在风格方面有内部标准,这可确保所有开发人员都轻松读懂其他开发人员的代码,但是这种趋势蔓延到了开发操作系统和可重用类库的公司,这也就意味着C++程序必须处理多种不同的命名规则。

6.定义对象:当创建好类时,就可以将其作为新类型来声明这种类型的变量。其声明方法与普通类型相同。例:Cat mimi。C++将类和对象区分,对象是类的实例。

7.访问类成员:定义类对象后,就可以使用句点运算符(.)来访问该对象的成员。例:mimi.Meow();调用对象mimi的成员函数Meow。

8.给对象赋值:例:mimi.itsAge = 2;这个操作就给对象mimi的itsAge赋值2。注:类内没有的成员或操作是不可以使用的,即对象没有这个属性。例如mimi.Bark(); 这个操作是错误的,因为类Cat没有Back这个操作。不应该把声明和定义混为一谈,声明是指出类是什么,定义是为对象分配内存。

9.类声明中的关键字:public(公开的)和private(私有的)用于类成员,其中私有成员只能在类方法中访问;共有成员可以通过类的任何对象进行访问。默认情况下,类成员都是私有的。

程序清单 6.1 访问一个简单类的公有成员

#include <iostream>

class Cat
{
    public:
        int itsAge;
        int itsWeight;
};

int main()
{
    Cat Frisk;
    Frisk.itsAge = 5;

    std::cout<<"Frisky is a cat who is ";
    std::cout<<Frisk.itsAge<<" years old."<<std::endl;

    return 0;
}
10.类的数据:通用的设计规则是,应该让类的数据成员为私有的。通过创建被存取方法(accessor method)的公有函数,用这些方法来设置和获取私有成员变量。在程序的其他地方调用它们来获取和设置私有成员变量。注:存取器函数将数据和存储细节和数据的使用细节分开,通过使用获取器函数,以后修改数据的存储方式时,不必重新编写使用这些数据的函数。

程序清单 6.2 包含存取器方法的类

#include <iostream>

class Cat
{
public:
    unsigned int GetAge();
    void SetAge(unsigned int Weight);

    unsigned int GetWeight();
    void SetWeight(unsigned int Weight);

    void Meow();

private:
    unsigned int itsAge;
    unsigned int itsWeight;
};
注:关键字class用于声明新类型,类是类成员数据的集合,类成员数据可以是各种类型的变量,包括其他类。类还包含类函数(方法),即用来操作类中的数据或为类提供其他服务的函数。定义类对象的方法与定义变量一样,首先指出类型(类)、然后是变量名(对象)、使用句点运算符(.)可访问类的成员和函数。

11.实现类方法:存取器函数提供了到类的私有成员数据的共有接口,每个存取器函数以及声明的其他类方法都必须有实现,实现被称为函数的定义。成员函数的定义类似于常规函数:首先指出函数的返回类型,如果函数不返回任何值,则使用void。然后是类名、两个冒号、函数名和参数。

程序清单 6.3 实现了一个简单类的方法

#include <iostream>

class Cat
{
public:
	int GetAge();
	void SetAge(int age);
	void Meow();
private:
	int itsAge;
};

int Cat::GetAge()
{
	return itsAge;
}

void Cat::SetAge(int age)
{
	itsAge = age;
}

void Cat::Meow()
{
	std::cout << "Meow" << std::endl;
}

int main()
{
	Cat Frisky;
	Frisky.SetAge(5);
	Frisky.Meow();
	std::cout << "Frisky is a cat who is ";
	std::cout << Frisky.GetAge() << " years old." << std::endl;
	Frisky.Meow();

	return 0;
}

12.构造函数和析构函数:构造函数可以根据需要接受参数,但它不能有返回值:连void都不行,构造函数是以恶搞与类同名的类方法。当声明构造函数后,还应该声明析构函数。构造函数创建并初始化类对象,而析构函数在对象被销毁后完成清理工作并释放(在构造函数或对象的生命周期中)分配的资源或内存,析构函数总是与类同名,但在前面加(~)。析构函数没有参数,也没有返回值。例:~ Cat();。

13.默认构造函数和析构函数:没有参数的构造函数称为默认构造函数。析构函数只能有一个,不带参数也没有返回值,可在析构函数中执行一些操作。当没有声明构造函数和析构函数时,编译器会提供一个默认构造函数和析构函数,但这个构造函数和析构函数什么都不做。所以尽量创建自己的默认构造函数和析构函数。

14.使用默认构造函数:不接受任何参数的构造函数,也可以给自己的默认构造函数提供函数体,在其中对对象进行初始化。出于格式方面的考虑,建议至少定义一个构造函数,将成员变量指定合适的默认值,以确保对象总是能正确的运行。

程序清单 6.4 使用构造函数和析构函数

#include <iostream>

class Cat
{
public:
	Cat(int initialAge);
	~Cat();
	int GetAge();
	void SetAge(int age);
	void Meow();
private:
	int itsAge;
};

Cat::Cat(int initialAge)
{
	itsAge = initialAge;
}

Cat::~Cat()
{
}

int Cat::GetAge()
{
	return itsAge;
}

void Cat::SetAge(int age)
{
	itsAge = age;
}

void Cat::Meow()
{
	std::cout << "Meow."<<std::endl;
}

int main()
{
	Cat Frisky(5);
	Frisky.Meow();
	std::cout << "Frisky is a cat who is ";
	std::cout << Frisky.GetAge() << " years old." << std::endl;
	Frisky.Meow();
	Frisky.SetAge(7);
	std::cout << "Now Frisky is ";
	std::cout << Frisky.GetAge() << " years old." << std::endl;

	getchar();

	return 0;
}
注:使用构造函数来初始化对象。在添加了构造函数后一定要有析构函数。

15..const成员函数:关键字const可以用来声明不可以被修改的变量。也可以使用const来声明不会修改任何类成员值的成员函数。要将类方法声明为const,可在方法声明中将所有参数括号和分号之间放置关键字const。例:void  SomeFunction()  const;注:如果将一个函数声明为const的,而该函数的实现通过修改某个成员变量的值而修改了对象,编译器将其视为错误。良好的编程习惯是,尽可能将方法声明为const的,这样可以让编译器捕获错误,减少bug。

16.接口与实现:客户是程序中创建和使用类对象的部分,可以将类的公有接口(类声明)视为类的行为方式。注:C++是强类型化(strongly typed)语言,当违反条款时,编译器将通过显示编译器错误强制执行合同。

程序清单 6.5 违反接口合同的例子

#include <iostream>

class Cat 
{
public:
	Cat(int initialAge);
	~Cat();
	int GetAge() const;
	void SetAge(int Age);
	void  Meow();
private:
	int itsAge;
};

Cat::Cat(int initialAge)
{
	itsAge = initialAge;
	std::cout << "Cat Constructor." << std::endl;
}

Cat::~Cat()
{
	std::cout << "Cat Constructor" << std::endl;
}

int Cat::GetAge() const
{
	return (itsAge++);
}

void Cat::SetAge(int age)
{
	itsAge = age;
}

void Cat::Meow()
{
	std::cout << "Meow." << std::endl;
}

int main()
{
	Cat Frisky;
	Frisky.Meow();
	Frisky.Bark();
	Frisky.itsAge = 7;

	return 0;
}
注:这个程序不可以编译,需要注意的是,编译错误比运行错误要很多,因为运行错误很可能藏得很深,很难发现,从而给调试带来很大的困难,为了减少不可预知的错误,
最好的是利用编译器发现错误。 

17.类声明和方法定义的位置:为类声明的每个函数都必须有定义,这种定义被称为函数实现,类方法的定义也由函数头和函数题组成。定义必须位于编译器能够找到的文件中,大多数C++编译器希望这种文件的扩展名为.c或.cpp。也可以将声明放在实现文件中,但这不是一种恨好的习惯,大多数程序员采用的约定是,将声明放在头文件中,该头文件的名称与现实文件相同,但扩展名是.h、.hp或.hpp。

18.内联实现:类的方法也可以作为内联的。为此只需要在返回类型前面加上关键字inline。或者将函数的定义放在类声明中,这是函数将自动成为内联函数。注:内联函数的函数体紧跟在类方法声明之后,圆括号后面没有分号。

程序清单 6.6 位于Cat.hpp中的Cat类声明 (程序清单6.6和6.7重新编写了Cat类,将类声明放在文件Cat。hpp中,函数实现在Cat.cpp中)

#include <iostream>

class Cat
{
public:
	Cat(int initiaiAge);
	~Cat();
	int GetAge()const
	{
		return itsAge;
	};

	void SetAge(int age)
	{
		itsAge = age;
	}

	void Meow() const
	{
		std::cout << "Meow." << std::endl;
	}
private:
	int itsAge;
};
程序清单 6.7  位于Cat.cpp中的Cat类实现

#include "Cat.hpp"

Cat::Cat(int initialAge)
{
	itsAge = initialAge;
}

Cat::~Cat()
{

}

int main()
{
	Cat Frisky(5);
	Frisky.Meow();
	std::cout << "Frisky is a cat who is ";
	std::cout << Frisky.GetAge() << " years old." << std::endl;
	Frisky.Meow();
	Frisky.SetAge(7);
	std::cout << "Now Frisky is ";
	std::cout << Frisky.GetAge() << " years old." << std::endl;

	return 0;
}

19.将他类用作成员数据的类:常见的创建复杂类的方式是,首先声明较简单的类,然后将其包含到较复杂类的声明中。

程序清单 6.8 声明一个完整的类

//Begin Rectangle.hpp
#include <iostream>
class Point   //holds x,y coordinates
{
	//no constructor,use default
public:
	void SetX(int x) { itsX = x; }
	void SetY(int y) { itsY = y; }
	int GetX()const { return itsX; }
	int GetY()const { return itsY; }
private:
	int itsX;
	int itsY;
};//end of point class declaration

class Rectangle
{
public:
	Rectangle(int top, int left, int bottom, int right);
	~Rectangle() {}

	int GetTop() const { return itsTop; }
	int GetLeft() const { return itsLeft; }
	int GetBottom() const { return itsBottom; }
	int GetRight() const { return itsRight; }

	Point GetUpperLeft() const { return itsUpperLeft; }
	Point GetLowerLeft() const { return itsLowerLeft; }
	Point GetUpperRight() const { return itsUpperRight; }
	Point GetLowerRight() const { return itsLowerRight; }

        void SetUpperLeft(Point Location) { itsUpperLeft = Location; }
	void SetLowerLeft(Point Location) { itsLowerLeft = Location; }
	void SetUpperRight(Point Location) { itsUpperRight = Location; }
	void SetLowerRight(Point Location) { itsLowerRight = Location; } 

	void SetTop(int top) { itsTop = top; }
	void SetLeft(int left) { itsLeft = left; }
	void SetBottom(int bottom) { itsBottom = bottom; }
	void SetRight(int right) { itsRight = right; }

	int GetArea() const;

private:
	Point itsUpperLeft;
	Point itsUpperRight;
	Point itsLowerLeft;
	Point itsLowerRight;
	int itsTop;
	int itsLeft;
	int itsBottom;
	int itsRight;
};
//end Rectangle.hpp
程序清单 6.9 RECT.cpp

//Begin Rect.cpp
#include "Rectangle.hpp"

Rectangle::Rectangle (int top,int left,int bottom,int right)
{
	itsTop = top;
	itsLeft = left;
	itsBottom = bottom;
	itsRight = right;

	itsUpperLeft.SetX(left);
	itsUpperLeft.SetY(top);

	itsUpperRight.SetX(right);
	itsUpperRight.SetY(top);

	itsLowerLeft.SetX(left);
	itsLowerLeft.SetY(bottom);

	itsLowerRight.SetX(right);
	itsLowerRight.SetY(bottom);
}
//compute area of rectangle by finding sides,
//establish width and height and then multiply

int Rectangle::GetArea()const
{
	int Width = itsRight - itsLeft;
	int Height = itsTop - itsBottom;
	return (Width * Height);
}

int main()
{
	//initialize a local Rectangle variable
	Rectangle MyRectangle(100, 20, 50, 80);

	int Area = MyRectangle.GetArea();

	std::cout << "Area: " << Area << "\n";
	std::cout << "Upper Left x Coordinate: ";
	std::cout << MyRectangle.GetUpperLeft().GetX();

	getchar();

	return 0;
}

注:在main函数中,MyRectangle.GetUpperLeft().GetX();这句话看起来很奇怪,但是这就是将GetUpperLeft()和GetX()组合在一起。声明只引入一个名称而不为其分配内存,而定义分配内存。

20.结构:与关键字class类型的关键字是struct。用来声明结构,在C++中,结构和类相同,只是将成员默认为公有的。可以像类一样声明结构,并给他声明数据成员和函数。事实上,如果遵循显示地声明类的公有和私有部分种良好的编程习惯,那么结构和类的声明方法没有什么区别。

第七部分 再谈程序流程

1.循环的鼻祖:goto。在计算机科学发展的早期,循环由标签、语句和跳转组成。在C++中,标签是后面跟冒号(:)的名称。标签放在合法C++语句的左边,跳转是通过关键字goto后面加上标签的名称实现的。

程序清单 7.1 使用关键字goto实现循环

#include<iostream>
int main()
{
	using namespace std;
	int counter = 0;

loop:
	counter++;
	cout << "counter:" << counter << endl;
	if (counter < 5)
		goto loop;

	cout << "Comolete.Counter:" << counter << endl;

	return 0;
}
注:作为一条原则,程序员应避免使用goto语句。因为goto语句可以向前或向后跳转到源代码的任何位置,不加选择的使用goto语句会导致程序可读性和维护性很差。

2.while循环:只要开始条件为真,则执行while循环体,不断重复直到条件为假时。所以循环必须要有终止条件。while循环测试的条件可以与任何合法C++表达式一样复杂。

3.continue和break简介:有时候,需要在执行while循环体总所有语句之前返回到while循环开头。continue语句用于跳转到循环开头。而在另一些时候,需要在满足循环退出条件之前跳出循环。break语句立即跳出while循环,继续执行右括号后的语句。

程序清单 7.2 break和continue

#include<iostream>

int main()
{
	using namespace std;

	unsigned short small;
	unsigned long large;
	unsigned long skip;
	unsigned long target;
	const unsigned short MaxSmall = 65535;

	cout << "Enter a small number:";
	cin >> small;
	cout << "Enter a large number:";
	cin >> large;
	cout << "Enter a skip number:";
	cin >> skip;
	cout << "Enter a target number:";
	cin >> target;

	cout << endl;

	while (small < large && small < MaxSmall)
	{
		small++;

		if (small % large == 0)
		{
			cout << "skipping on" << small << endl;
			continue;
		}

		if (large == target)
		{
			cout << "Target reached!";
			break;
		}

		large -= 2;
	}

	cout << endl << "Small:" << small << "Large:" << large << endl;

	return 0;
}
注:应慎用continue和break语句,因为他们的危险性仅次于goto语句。原因与goto语句相同。

4.while(true)循环:while循环测试条件可以是任何合法的C++表达式,只要条件为真,while循环将继续执行,可以将ture用作测试条件来创建永不结束的循环。

程序清单 7.5 条件永远成立的while循环

#include<iostream>
int main()
{
	int counter = 0;

	while(true)
	{
		counter++;
		if (counter > 10)
			break;
	}
	
	std::cout << "Counter:" << counter << std::endl;

	return 0;
}
5.实现do...while循环:while循环体可能永远都不会执行。while语句在执行循环体之前检查条件,如果条件为假,将跳出整个while循环体。

程序清单 7.6

#include<iostream>
int main()
{
	using namespace std;

	int counter;
	cout << "How many hellos?";
	cin >> counter;
	do
	{
		cout << "Hello" << endl;
		counter--;
	} while (counter > 0);
	cout << "Counter is:" << counter << endl;

	return 0;
}
注:如果期望循环体至少执行一次,请使用do..while。如果期望初始条件为假时不执行循环体,使用while循环。

6.for循环:使用while循环进行编程时,通常需要3步:设置初始条件、测试条件真假、在每次循环中修改条件变量。for循环将这三个步骤合并在一条语句中。for语句由关键字for和一对括号组成。括号中是3条用分号分隔的语句:for(initialization;test;action){statement}。第一个表达式initialization为初始条件,可以是任何合法的C++语句,但通常用于创建并初始化一个计数变量。第二个表达式test进行测试,可以是任何合法的C++表达式,其功能与while循环中的条件相同。第三个表达式action是要执行的操作,虽然可以是任何合法的C++语句,但是通常是将计数变量递增或递减。

程序清单 7.7 for循环

#include <iostream>
int main()
{
	int counter;
	for (counter = 0; counter < 5; counter++)
		std::cout << "Looping!";

	std::cout << std::endl << "Counter:" << counter << std::endl;

	return 0;
}
7.高级for循环:for语句功能强大且灵活,三条独立语句有多种变体。(1)可对多个变量进行初始化和递增(用逗号分开);(2)在for语句中使用空语句(三个独立语句都可以为空)。

8.空for循环:当不需要循环体做任何事情时,必须放一条空语句(     ;)。分号可以与循环头位于同一行。注:不要给三个条件语句加太多东西,避免循环条件过于复杂难于理解。

9.嵌套循环:任何循环都可以嵌套到另一个循环中。外层循环每执行一次,内层循环将完整执行一遍。

程序清单 7.8 嵌套for循环











你可能感兴趣的:(《21天学通C++(第五版)》学习笔记 [美] Jesse Liberty Bradley Jones著)