字符序列:表示单个字符常量用单引号,双引号表示一串连续字符的常量。由双引号引起来的字符串末尾总会被自动加上一个空字符('\0')。
strcpy在 函数库cstring(string.h)中被定义。
cin >> buffer; cin.getline(char buffer[], int length, char delimiter = '\n'); 两种方法都可以接收用户输入,但使用cin时具有以下局限性:1、只能接收单独的词(而不是完整的句子),因为这种方法以任何空白字符作为分隔符,包括空格spaces,跳跃符tabulators,换行符newlines和回车符arriage returns。2、cin不能给buffer指定容量,这使得程序不稳定,如果用户输入超出数组长度,输入信息会被丢失。
字符串和其它数据类型的转换:函数库cstdlib(stdlib.h)提供了3个有用的函数:1、atoi:将字符串string转换为整形int。2、atol:将字符串string转换长整形long。3、atof:将字符串string转换为浮点型float。例如:A=atoi(buffer);
函数库cstring(string)提供了几个处理字符串的函数:strcat、strcmp、strcpy、strlen
在指针声明时的星号asterisk(*)仅表示这里声明的是一个指针,不要和引用操作符混淆,虽然那也是写成一个星号(*)。它们只是同一个符号的两个不同任务。当给一个指针赋值时,我们总是赋给它一个地址值,而不是它所指向数据的值。如:int *a=&b;
数组的概念与指针的概念联系非常紧密。其实数组的标识相当于它的第一个元素的地址,就像一个指针相当于它所指向的第一个元素的地址,因此其实它们是同一个东西。数组名是一个指针常量(constant pointer),而不是指针变量,常量标识不可以被赋予其它数值。
在数组中我们使用了括号[]来指明我们要引用的数组元素的索引(index)。中括号也叫位移(offset)操作符,它相当于在指针中的地址上加上括号中的数字。例如,下面的两个表达式互相等价:a[3]=3; *(a+3)=3; 无论a是一个指针还是一个数组名,这两个表达式都是合法的。
递增(++)和递减(--)操作符比引用操作符reference operate(*)有更高的优先级
sizeof是C++的一个操作符,用来返回其参数的长度字节数常量。例如,sizeof(char)返回1,因为char类型是1字节长数据类型。
C++允许对指向函数的指针进行操作。它最大的作用是将一个函数作为参数传递给另外一个函数。声明一个函数指针像声明一个函数原型一样,除了函数的名字需要被括在括号内并在前面加星号asterisk(*)。
操作符new的存在是为了要求动态内存。new后面跟一个数据类型,并跟一对可选的方括号[]里面为要求的元素数。操作符new返回一个指向内存快开始位置的指针。
动态内存分配:操作符new和delete是C++执行指令,依赖于库(stdlib.h)。ANSI-C中的动态内存管理:1、函数malloc给指针动态分配内存的通用函数,原型为void *malloc(size_t nbytes);2、函数calloc与malloc在操作上非常像,原型为void *calloc(size_t nelements,size_t size);它接收2个参数,第一个参数是元素的个数,第二个参数表示每个元素的长度。相乘得到所需内存的总长度。3、函数realloc用来改变已经分配给一个指针的内存的长度,原型void *realloc(void *pointer,size_t size);4、函数free用来释放被前面malloc、calloc,realloc所分配的内存块,原型void free(void * pointer);
include之前加#:C中预处理器并不属于编译器的一部分,C预处理使用不同的语法,所有指令在C预处理器中都以#开头,#表示预处理指令开始执行。
数据结构(Data Structures)是组合到同一定义下的一组不同类型的数据,各个数据类型的长度可能不同。数据结构通常被用来建立数据库,特别是当我们考虑结构数组的时候。
就像其它数据类型一样,结构也可以有指针。其规则同其它基本数据类型一样:指针必须声明为一个指向结构的指针。
上面的数据结构的概念与C语言中结构概念是一样的,然而,在C++中,结构的概念已经被扩展到与类(class)相同的程度,只是它所有的元素都是公开的(public)。
定义字节的数据类型(tyepdef):C++允许我们在现有数据类型的基础上定义我们自己的数据类型,使用关键字typedef来实现这种定义,它的形式是 typedef existing_type new_type_name; 如果在一个程序中我们反复使用一个数据类型,而在以后的版本中我们有可能改变该数据类型的情况下,typedef就很有用了。或者如果一种数据类型的名称太长,你想用一个比较短的名字来代替,也可以使用typedef。
联合(Union)使得同一段内存可以被按照不同的数据类型来访问,数据实际是存储在同一位置的。它的声明和使用看起来与结构(structure)十分相似,但实际功能完全不同。union中的所有被声明的元素占据同一段内存空间,其大小取声明中最长元素的大小。union中
枚举(Enumerations)enum 可以用来生成一些任意类型的数据,不只限于数字类型或字符类型,甚至常量true和false。例如 enum colors_t{blue,green,red,yellow}; 注意在这个定义里我们没有使用任何基本数据类型,换句话说,我们创造了一种新的数据类型,而它并没有基于任何已存在的数据类型:类型color_t,花括号{}中包含了它的所有的可能的值。实际上,我们的枚举类型数据类型在编译时是被编译为整形数值的,而它的数值列表可以是任何指定的整形常量。如果没有指定常量,枚举中第一个列出的可能值为0,后面的每一个值为前面一个值加1。因此前面定义的数据类型colors_t中,blue相当于0,green相当于1,后面一次类推。如果我们在定义枚举数据类型的时候明确指定某些可能值(例如第一个)的等价整数值,后面的数值将会在此基础上增加。
构造函数和析构函数:对象(object)在生成过程中通常需要初始化变量或分配动态内存,以便我们能够操作,或防止在执行过程中返回意外结果。为了避免这种情况发生,一个class可以包含一个特殊的函数:构造函数constructor,它可以通过声明一个与class同名的函数来定义。当且仅当要生成一个class的新的实例instance的时候,也就是当且仅当声明一个新的对象,或给该class的一个对象分配内存的时候,这个构造函数将自动被调用。
构造函数的原型和实现中都没有返回值(return value),也没有void类型声明。构造函数必须这样写。
析构函数Destructor完成相反的功能。它在objects被从内存中释放的时候被自动调用。释放可能是因为它存在的范围已经结束了,或者是因为它是一个动态分配的对象,而被使用操作符delete了。析构函数必须与class同名,加水波号tilde(~)前缀,必须没有返回值。析构函数特别适用于当一个对象被动态分配内存空间,而在对象被摧毁的时候我们希望释放它所占用的空间的时候。
实际上,当我们定义一个class而没有明确定义构造函数的时候,编译器会自动假设两个重载的构造函数:1、Empty constructor,它是一个没有任何参数的构造函数,被定义为nop(没有语句),它什么都不做,模型:CExample::CExample(){}; 2、Copy constructor,它是只有一个参数的构造函数,该参数是这个class的一个对象,这个函数的功能是将被传入的对象(object)的所有非静态(non-static)成员变量的值都复制给自身这个object,模型:CExample::CExample(const CExample& rv){a=rv.a; b=rv.b; c=rv.c;}。必须注意:这两个默认构造函数(empty constructor和copy constructor)只有在没有其他构造函数被明确定义的情况下才存在。如果任何其它有任意参数的构造函数被定义了,这两个构造函数就不存在了,此时要使用empty constructor和copy constructor,就必需自己定义它们。
注意:当声明一个新的对象(object)的时候,如果不想传入参数,则不需要括号(); Class a;//right Class a();//wrong
类得指针(Pointers to classes) 类也是可以有指针的,要定义类得指针,我们只需要认识到,类一旦被定义就成为一种有效的数据类型,因此只需要用类的名字作为指针的名字就可以了。例如:CLASS *A; 。就像数据结构中的情况一样,要想直接引用一个由指针指向的对象(object)中的成员,需要使用操作符-> 。
(*x).y 和 x->y 的意思一样,表示有x指向的对象成员y。 x.y表示对象x的成员y。
有关键字struct和union定义的类:类不仅可以用关键字class定义,也可以用struct和union定义。因为在C++中类和数据结构的概念太相似了,所以这两个关键字struct和class的作用几乎是一样的(也就是说在C++中struct定义的类也可以有成员函数,而不仅仅有数据成员)。两者定义的类得唯一区别在于有class定义的类所有成员的默认访问权限是private,而struct定义的类所有成员默认访问权限为public,除此之外,两个关键字的作用是相同的。union的概念与struct和class定义的类不同,因为union台同一时间只能存储一个数据成员。但是有union定义的类也是可以有成员函数的。union定义的类得访问权限默认为public。
extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行。”
关键字this通常被用在一个class内部,指正在被执行的该class的对象(object)在内存中的地址。它是一个指针,其值永远是自身object的地址。它可以被用来检查传入的成员函数的参数是否是该对象本身。this还经常被用在成员函数operator=中,用来返回对象的指针(避免使用临时对象)。
静态成员(static members) 一个class可以包含静态成员,可以是数据,也可以是函数。一个class的静态数据成员被称作类变量"class variables",因为他们的内容不依赖于某个对象,对同一个class的所有object具有相同的值。静态成员对同一个class的所有object是同一个值,所以它可以被作为该class的任何object的成员所引用,或者直接被作为class的成员引用(当然这只适用于static成员)。
就像我们会在class中包含static数据一样,我们也可以使用它包含static函数。他们表示相同的含义:static函数是全域函数(global functions),但是像一个指定class的对象成员一样被调用。它们只能够引用static数据,永远不能引用class的非静态(nonstatic)成员。它们也不能够使用关键字this,因为this实际引用一个对象指针,但这些static函数却不是任何object的成员,而是class的直接成员。
类之间的关系(Relationships between classes):1、友元函数,通过在一个class中使用关键字friend,可以允许类的外部函数获得访问class的protected和private成员。2、友元类(Friend classes)就像我们可以定义一个friend函数,我们也可以定义一个class是另一个的friend,以便允许第二个class访问第一个class的protected和private成员。这里需要考虑到,如果没有特别指明,友元关系(friendship)并不是相互的。如在A中定义friend class B;则B可以访问A中的对象。3、类之间的继承(Inheritance between classes) 类的一个重要特征是继承,这使得我们可以基于一个类生成另一个类得对象,以便后者拥有前者的某些成员,再加上他自己的一些成员。由其它类引申而来的子类继承基类的所有可视成员。要定义一个类的子类,我们必须在子类的声明中使用冒号(colon)操作符":" ,如下所示:class derived_class_name:public base_clase_name; 这里derived_class_name为子类(derived class)名称,base_class_name为基类(base class)名称。public也可以根据需要换为protected和private,描述类被继承的成员的访问权限。标识符protected和private类似,他们的唯一区别在继承时才表现出来。当定义一个子类的时候,基类的protected成员可以被子类的其它成员所使用,然而private成员不可以。然而定义子类的时候使用的关键字public表示新类从基类所继承的成员必须获得最低程度保护。这种被继承成员的访问限制的最低程度可以通过使用protected或private而不是public来改变。使用protected时,基类中的所有public成员到子类中时将会成为protected成员,当然子类可以有自己的public成员。使用private时,除了子类自身外,其它任何程序都不能访问那些从基类继承而来的成员。有个没有明确写出访问限制,所有由关键字class生成的类被默认为private,而所有由关键字struct生成的类被默认为public。
理论上说,子类继承了基类的所有函数,除了:1、构造函数和析构函数。2、operator=()成员。3、friends。 虽然基类的构造函数和析构函数没有被继承,但是当一个子类的object被生成或注销的时候,其基类的默认构造函数和默认析构函数总是被自动调用的。如果基类没有默认构造函数,或你希望当子类生成新的object时,基类的某个重载函数被调用,你需要在子类的每个构造函数的定义中指定它:derived_class_name(parameters):base_class_name(parameters){}
多重继承:一个class可以从多个class中继承属性或函数,只需要在子类的声明中使用逗号将不同的基类分开就可以了。
基类的指针:继承的好处之一是一个指向子类的指针与一个指向基类的指针是type-compatible的。
虚拟成员(Virtual members):如果想在基类中定义一个成员留待子类中进行细化,我们必须在它前面加关键字virtual,以便可以使用指针对指向相应的对象进行操作。关键字virtual的作用就是在当使用基类的指针的时候,使子类中与基类同名的成员在适当的时候被调用。
抽象基类(Abstract base classes):基本抽象类可以在函数的声明后面直接写“=0”。例如 virtual int area(void)=0; 这种函数被称为纯虚拟函数(pure virtual function),而所有包含纯虚拟函数的类被称为抽象基类。抽象基类的最大不同是它不能有实例(对象),但可以定义指向它的指针。
makefile用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新编译,但是不是所有的文件都需要重新编译,makefile中记录有文件的信息,在make时会决定在链接的时候需要重新编译哪些文件。makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖的文件有了改变,编译器会自动发现最终的生成文件已经过时,而重新编译相应的模块。
GDB can do four main kinds of things (plus other things in support of these) to help you catch bugs in the act: (1):Start your program,specifying anything that maight affect its behavior.(2):Make your program stop on specified conditions.(3):Examine what has happened,when your program has stoped.(4):Change thing in your program,so you can experiment with correcting the effects of one bugs and go on learn about anther.
同样是x86的cpu,但是可以用不同形式的汇编语言来表示。在windows上面我们使用的更多的是intel格式的汇编语言,而在linux系统上面我们使用的更多的常常是AT&T格式的汇编语言。
网络的不同层次实现网络的不同功能。物理层主要实现报文的成侦处理;数据链路层完成对报文的优先级的管理,同时实现二层转发和流量控制;网络层实现路由和转发的功能,一方面它需要实现对报文的fragment处理,另外一方面它还需要对路由信息进行处理和保存;传输层实现报文的发送和接受,它利用计数、时序、定时器、重发等机制实现对报文的准确发送,当然这都是tcp的发送机制,而udp一般是不保证报文发送和接收的;应用层就是根据传输层的端口信息调试用不同的程序来处理传输的内容。物理层关心的是如何将电气信号变成一段报文;数据链路层关心的是mac地址、vlan、优先级等;网络层关心的是ip地址,下一跳ip;传输层关心的是端口资源;应用层关心的是报文组装、解析、渲染、存储、执行等等。
真正内核的东西:cpu初始化、线程调度、内存分配、文件系统、网络协议栈、驱动等。
如果我们需要cpu做很少的事,或者只有一件事情时,那么根本没有设计操作系统的必要。设计操作系统,主要是想在单位时间内完成几件事情。所以操作系统就是为了共享资源而存在的。
单总线(系统总线)按总线的不同又可细分为:地址总线,数据总线和控制总线。1、地址总线又来传输有CPU向主存、外设发送的地址信息,其位数决定了系统能够使用的最大存储容量。2、数据总线:用来传输各功能部件之间的数据信息,其位数是决定系统总体性能的关键因素。控制总线:传输的是控制信息,包括CPU送出的控制命令和主存(或外设)返回CPU的反馈信息。
地址总线、数据总线和控制总线是系统总线的一部分,只是根据总线上传送的信息不同而分别定名,不能因为它们的名称不同而认为它们是3个总线。
机器字长也称为基本字长,它是指参与运算的数的基本位数,也即CPU在同一时间内一次处理的二进制的位数。机器字长越长,操作数的位数越多,计算精度就越高,但相应不见的位数也会增多,硬件成本也越高。
数据通路宽度:是指数据总线一次所能并行传送信息的位数,它影响计算机的有效处理速度。数据通路宽度分为CPU内部和CPU外部两种情况。CPU内部数据通路宽度等于机器字长,即内部数据线的位数;CPU外部数据通路宽度则等于系统数据总线一次所能并行传送信息的位数,即CPU与主存,输入输出设备之间一次数据传送的信息位数。
计算机系统的主要技术指标有:机器字长、数据通路宽度、主存容量和运算速度等。1、机器字长是指参与运算的数的基本位数,它有加法器、寄存器的位数决定的。2、数据通路宽度是指数据总线一次所能存储的全部的位数。主存容量是指主存储器所能存储的全部信息量。3、运算速度与机器的主频、执行什么样的操作、主存本身的速度等许多因素有关。
模板(Template)使得我们可以生产通用的函数,这些函数能够接受任意数据类型的参数,可以返回任意类型的值,而不需要对所有可能的数据类型进行函数重载。这在一定程度上实现了宏(macro)的作用。它们的原型定义可以是下面两种中的任何一个:1、templatefunction_declaration; 2、templatefunction_declaration; 这两种原型定义的不同之处在关键字class或typename的使用,它们实际是完全等价的,因为两种表达式的意思和执行都一摸一样。 例如,要生产一个模板,返回两个对象中较大的一个,可以这样写:templategenericType getMax(genericType a,genericType b){return(a>b)?a:b;} 其中genericType仍没有代表任何具体的数据类型;当函数getMax被调用的时候,我们可以使用任何有效的数据类型来调用它,这个数据类型将被作为pattern来代替函数中genericType出现的地方。
也可以定义类模板(class template),使得一个类可以有基于通用类型的成员,而不需要在类生成的时候定义具体的数据类型。
模板特殊化(Template specialization):模板特殊化是当模板中的pattern有确定的类型时,模板有一个具体的实现。模板特殊化由以下格式定义:template<>class class_name 这个特殊化本身也是模板定义的一部分,因此我们必须在该定义开头写template<>,而且因为它确实为一个具体类型的特殊定义,通用数据类型在这里不能使用,所以第一对尖括号<>内必须为空。在类名称后面,我们必须将这个特殊化中使用的具体数据类型写在尖括号中<>中。当我们特殊化模板的一个数据类型的时候,同时还必须重新定义类得所有成员的特殊化实现,即在特殊化的定义中包含它自己的构造函数constructor,虽然它与通用模板中的构造函数是一样的,这样做的原因就是特殊化不会继承通用模板的任何一个成员。
模板与多文件工程 (Templates and multiple-file projects):从编译器的角度来看,模板不同于一般的函数或类。它们在需要时才被编译(compiled on demand),也就是说一个模板的代码直到需要生成一个对象的时候(instantiation)才被编译。当需要instantiation的时候,编译器根据模板为特定的调用数据类型生成一个特殊的函数。当工程变得越来越大的时候,程序代码通常会被分为多个源程序文件。在这种情况下,通常接口(interface)和实现(implementation)是分开的。用一个函数库做例子,接口通常包括所有能被调用的函数的原型定义。它们通常被定义在以.h 为扩展名的头文件 (header file) 中;而实现 (函数的定义) 则在独立的C++代码文件中。模板这种类似宏(macro-like) 的功能,对多文件工程有一定的限制:函数或类模板的实现 (定义) 必须与原型声明在同一个文件中。也就是说我们不能再 将接口(interface)存储在单独的头文件中,而必须将接口和实现放在使用模板的同一个文件中。回到函数库的例子,如果我们想要建立一个函数模板的库,我们不能再使用头文件(.h) ,取而代之,我们应该生成一个模板文件(template file),将函数模板的接口和实现都放在这个文件中 (这种文件没有惯用扩展名,除了不要使用.h扩展名或不要不加任何扩展名)。在一个工程中多次包含同时具有声明和实现的模板文件并不会产生链接错误 (linkage errors),因为它们只有在需要时才被编译,而兼容模板的编译器应该已经考虑到这种情况,不会生成重复的代码。
通过使用名空间(Namespaces)我们可以将一组全局有效的类、对象或函数组织到一个名字下面。换种说法,就是它将全局范围分成许多子域范围,每一个子域范围叫做一个名空间(namespace)。名空间的作用在于全局对象或函数很有可能重名而造成重复定义的错误,名空间的使用可以避免这些错误的发生。
名空间的使用(using namespace):使用using指令后面跟namespace可以将当前的嵌套层与一个指定的名空间连在一起,以便使该名空间下定义的对象和函数可以被访问,就好像它们是在全局范围内被定义的一样。它的使用遵循以下原型定义:using namespace identifer; 这里需要注意,语句usingnamespace只在其被声明的语句快内有效,如果using namespace是在全局范围内被声明的,则在所有代码中都有效。
别名定义(alias definition)我们可以为已经存在的名空间定义别名,格式:namespace new_name=current_name;
引入的3种操作符能够帮助我们处理出错情况:try,throw和catch。它们所进行的操作是:tey语句块中的代码被正常执行,如果有例外发生,代码必须使用关键字throw和一个参数来扔出一个例外,这个参数可以是任何有效的数据类型,它的类型反映了例外的特征。如果有例外发生,也就是说在try语句块中有一个throw指令被执行了,则catch语句块会被执行,用来接收throw传来的例外参数。
catch语句块必须紧跟在try语句块后面,中间不能有其它的代码,catch捕获得参数可以是任何有效的数据类型。
传统的类型转换符进行简单对象的类型转换:如int i;double d;i=(int)d;或i=int(d); 。同样的操作也可以被用在类或类得指针上。
ANSI-C++标准定义了4中新的类型转换操作符,使用同样的格式:1、reinterpret_cast(expression) 可以将一个指针转换为任意其它类型的指针,它可以用来将一个指针转换为一个整形,反之亦然,这个操作符可以在不相关的类之间进行指针转换,操作的结果是简单的将一个指针的二进制数据(binary copy)复制到另一个指针,对指针指向的内容不做任何检查或转换; 2、dynamic_cast(expression) 3、static_cast(expression) 可以执行所有能够隐含执行的类型转换,以及它们的反向操作(即使这种方向操作是不允许隐含执行的)完全用来进行指针的操作,它可以用来进行任何可以隐含进行的转换操作以及它们被用于多态类情况下的方向操作, 4、const_cast(expression) 这种类型转换对常量const进行设置或取消操作,例如:class C{}; const C*a=new C; C*b=const_cast(a); 其它三种cast操作符都不可以修改一个对象的常量属性(constness)。 这里new_type是要转换成的目标类型,expression是要被转换的内容。
ANSI-C++还定义了一个新的操作符叫做typeid,包含在头文件中,用于检查一个表达式的类型:typeid(expression); 这个操作符返回一个类型为type_info的常量指针,这种类型定义在标准头文件中。这种返回值可以用操作符==和!=来互相进行比较,也可以用来通过name()函数获得一个描述数据类型或类名称的字符串。
要通过一个流对象打开一个文件,我们使用它的成员函数open(),例如:void open(const char *filename,openmode mode); 这里filename是一个字符串,代表要打开的文件名,mode是以下标识符的一个组合:1、ios::in为输入(读)而打开文件 2、ios::out为输出(写)而打开文件 3、ios::ate初始位置:文件尾 4、ios::app所有输出附加在文件末尾 5、ios::trunc如果文件已存在则先删除该文件 6、ios::binary二进制方式。这些标识符可以组合使用,中间以“或”操作符(|)间隔。
可以通过调用成员函数is_open()来检查一个文件是否已经被顺利打开了:bool is_open();它返回一个bool值,true代表文件已经被顺利打开,false则相反。
成员函数eof是ifstream从类ios中继承过来的,当达到文件末尾时返回true。状态标识符验证(verification of stage flags):eof();bad()如果读写过程出错,返回true;fail()除了与bad()同样的情况下回返回true以外,格式错误也会返回true,例如当想读入一个整数,而获得一个字母的时候;good()这是最通用的,如果调用以上任何一个函数返回true的话,此函数返回false。要想重置以上成员函数所检查的状态标志,可以使用成员函数clear(),没有参数。
阅读(1492) | 评论(0) | 转发(0) |