C++程序设计基础教程_第1页
C++程序设计基础教程_第2页
C++程序设计基础教程_第3页
C++程序设计基础教程_第4页
C++程序设计基础教程_第5页
已阅读5页,还剩341页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

,第二期更新内容C+11/14标准Lambda、static_assert,typetraits,Movesemantics。WIN32消息,绘图,控件,资源,文件,内存,进程,线程。,起源,语言特点,语言缺点,C+语言及相关软件介绍,day01,前景与方向,编译器安装与配置,由C+起源:1974年Bjarne博士在分析与研究UNIX系统由与内核分布面造成的网络流量时试图寻找一种有效工具使其更加模块化他在c的增加了类似Simula的类的机制并与1983年开发一种新的语言C+;,C+的语言特点1兼容c且继承了c的特性并同c一样高效且可移植2属于面向对象的编程抽象封装继承多态3语言灵活(类的层次结构设计)且支持指针3支持运算符重载4异常处理机制5支持泛型编程Tf(Tx)returnx*x;6多种类库的支持,语言缺点:语言复杂支持多种设计风格复杂的c+程序正确性不易保证C+的发展方向windows平台unix平台嵌入式,C+也是一种编译型的语言推荐使用vs2013编译环境其他可选vc6.0vs2010nodepad+vs2013基本支持c11标准vs2010以上Vs2013是微软公司的一款软件开发平台IDE(集成开发环境)Vs2013的安装使用1安装前需要先安装IE10提供支持2下载安装包,4安装vs2013,5安装完成(20分钟左右),如何建立一个c+程序文件-新建-(visualc+类型)+win32consoleapplication自定义文件名文件路径-应用程序设置-其他选项-空工程-源文件-新建条目-c+文件-添加-完成,C+的头文件,名字空间,简单的i/0函数,一个简单的c+程序,day02,C+风格的操作,Vs2013基本设置行号设置:工具-选项-文本编辑器-所有语言-行号选中快捷键:1复制:如果你想复制一整行代码,只需将光标移至该行,再使用组合键“Ctrl+C”来完成复制操作,而无需选择整行2剪切:如果你想剪切一整行代码,只需将光标移至该行,再使用组合键“Ctrl+X”来完成剪切操作,而无需选择整行。,3删除:如果你想删除一整行代码,只需将光标移至该行,再使用组合键“Ctrl+L”来完成剪切操作,而无需选择整行。4粘贴:如果你想粘贴你已经复制的内容将光标移至该行再使用组合键“Ctrl+v”来完成粘贴操作5撤销:使用组合键“Ctrl+Z”进行撤销操作;6反撤销:使用组合键“Ctrl+Y”进行反撤销操作。7查找:Ctrl+f(Ctrl+H替换)8移动光标:home行首end行尾,调试相关1)调试(启动):F5;(调试器)2)ctrl+s保存修改4)调试(逐语句):F11;5)调试(逐过程):F10;6)设置断点:F9。7)调试不执行:F7,1创建源文件c+的源文件的扩展名使用:.cpp.cc.C.cxx2#include为c+标准库的i/o函数的头文件c+旧式风格:iostream.hc+新式风格:iostreamcstring3usingnamespacestd;using编译指令指定使用的名字空间namespacestd标准名字空间,4cina;cout,导入c头文件#include#include”add.h”/h可以不加其他库函数包含对应的头文件正常使用例:strcpy(a,b);编译:终端窗口:gccap1.cpplstdc+g+ap1.cppvs开发环境:F7相同,名字空间的引入,定义,使用,名字空间,day03,嵌套,无名名字空间,名字空间的引入:在C+中名称可以是变量函数结构体类及类的相关成员使用第三方类库时极有可能出现名称冲突c+利用作用域的特性引入了名字空间的概念名字空间:名字空间是一种描述逻辑分组的机制send(sendEx)sendsendEx防止命名冲突跨文件访问注意:名字空间可以是全局的可以是位于另一个名称空间中不可以存在于代码块中,1定义:namespaceAintI;doubled;voidshow();namespaceBintIfloatf;,A中的I与B中的I并不发生冲突2相同名字空间可以多次添加补充名字空间的内容namespaceAshow().;非用户定义的名称空间存在于全局命名空间,intI;使用:A:I=0;B:I=1;:I=2;/全局命名空间:为作用域解析运算符(域运算符),1Using:为了避免每次都使用名字空间时都要其进行名字空间限定可以使用using对名字空间内的内容进行特定区域的声明usingA:I;/using声明std:cout再次使用可以直接调用名称而无需增加空间限定这种声明可以全局全局域同名变量std:coutintInamespaceAintimain2using:作为编译指令的使用使对应名字空间内的所有内容可用且使用时可以省略作用域解析运算符usingnamespaceA;,注意事项:1避免歧义usingA:I;usingB:I;I=1;/?哪一个?2不要用using声明相同名称的内容usingA:I;intI;3using做编译指令时名称空间为全局如果局部于名称空间的名字相同则局部覆盖名字空间内容namespaceexterninti4局部名称会覆盖全局与using指令的名字空间内容但可以用I(局部):I(全局)A:I(名字空间)区分使用5using声明比using编译安全,声明是指定具体内容编译器发出指示编译指令导入全部名字空间内容可能发生冲突覆盖时编译器不会发出警告,名字空间的嵌套,NamespaceAinta;namespaceBintb;访问:A:B:b=1;,未命名名字空间:内部链接特性与static修饰的内容特性相同Namespaceinta;coutaendl;补充:1Iostream相关函数存放在std的名字空间内UsingStd:coutintIntchar,5.3const_cast(源类型变量)去除指针或引用上的const属性voidfoo(constint*i)5.4reinterpret_cast(源类型变量)任意类型的指针或引用或整型之间的转换但不可改变const属性其他限制是不能转换成比指针更小的类型不能将函数指针与其他类型进行转换5.5dynamic_cast(源类型变量)多态父子类指针或引用之间的转换动态转换,New的使用方法,Delete使用方法,Lambda表达式,动态内存分配/lambda表达式,day09,C+在添加动态分配运算符newdelete的同时,支持标准C的动态分配库函数语法:1type_name*pointer_name=newtype_name;/未初始化说明:与malloc不同new会根据类型的不同自动分配对应的内存大小且返回对应类型的指针2type_name*pointer_name=newtype_name(value)/初始化3type_name*pointer_name=newtype_name()/初始化为零4type_name*pointer_name=newtype_namen分配n个type_name的数据的内存大小,4type_name*pointer_name=newtype_namen=1,2,3,4.c+0 x标准初始化数组时进行初始化5deletepointer_name与非new配对使用6deletepointer_name与new配对使用某些编译器在分配数组的大小时会在所分配的内存前多加4个字节大小用于存放分配的数组大小7对new分配内存是否失败的判断new操作符会抛出bad_alloc异常,C+之父给C程序员的建议1.1尽量的少使用宏使用inline函数替代带参的宏使用constenum去替代常量宏1.2使用namespace去避免命名冲突1.3变量随时用随时定义以保证初始化1.4尽量少使用强制类型转换如果必须转换就使用四个转换运算符中的一个1.5少使用mallocfree因为newdelete会做的更好1.6尽量少使用C风格的字符串因为string会使用更方便1.7逐步建立面向对象的思想,Lambda表达式”(lambdaexpression)是一个匿名函数,即没有函数名的函数。,Capture子句Lambda可在其主体中引入新的变量(用C+14),它还可以访问(或“捕获”)周边范围内的变量。1.var表示值传递方式捕捉变量var;2.=表示值传递方式捕捉所有父作用域的变量(包括this);3.stringmajor;doublescore;行为:voidstudy();voidexam();voidplay();,2类:具有相同的属性和行为的对象被分成一组即为一个类类在c+中为一种自定义复合类型:成员变量成员函数(方法)3对象:类是对现实世界的抽象对象则是类在程序中的一个虚拟的实例可以用类来声明变量(也称为实例)。每个实例是类的一个对象4使用类来描述一个类struct/classSTUDENT访问控制限定符:/struct无stringname;intstuID;,doublescore;voidstudy();voidexam();voidplay();5实例化:定义类的实例可以称为类的实例化STUDENTstudent;6struct定义类成员默认为公开class定义类成员默认为私有,7成员控制限定符public:公有成员任意访问protected:保护成员只有本类成员和子类可以访问private:私有成员只有本类成员可以访问对不同成员的访问控制属性加以区分体现了c+作为面向对象程序设计语言的封装特性8类的声明与实现可以分开(分开后则不能形成内联),如何初始化对象,创建过程,对象的创建过程,day11,如何初始化成员对象?(因为私有成员的存在)C+为类的初始化提供了一个特有的函数-构造函数构造函数的作用:对对象的成员的初始化构造函数的特性:1构造函数与类型名相同2当创建一个对象时会被自动调用(仅调用一次)3构造函数没有返回值类型4.1如果不提供构造函数编译器则自动提供一个无参构造函数4.2但提供构造函数编译器会自动回收默认的构造函数4.3缺省构造构造对基本类型不做初始化类类型调用相应的缺省,5构造函数可以进行重载6构造函数的参数可以是默认值,但默认值必需靠右编写一般可以通过构造函数参数的默认值简化构造函数的个数,对象的创建过程1为整个对象分配内存空间2以构造实参调用构造函数2.1构造基类部分(无基类忽略)2.2构造成员变量(如果是类类型则构建这个成员)2.3执行构造代码(函数只放在代码区)3在栈中创建单个对象类名对象;/注意不加空括号Aa;,类名对象(实参表)4在栈中创建对象数组类名对象数组元素个数类名对象数组元素个数=类名(实参表),;类名对象数组=类名(实参表),;A*arr10=newA10A(1),A(2);实现一个时钟类,初始化列表,类型转换构造,拷贝构造,初始化列表,析构,单参构造函数,day12,1构造函数的初始化列表A(inti):.i(i).,.m_i=i;与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段2为什么使用初始化列表初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作/2.1常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面2.2引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面,2.3成员变量的初始化顺序:按照成员定义的顺序进行初始化(与初始化表的顺序无关)2.4类的类型成员变量和基类子对象必须在初始化列表中初始化,否则将调用相应类型的缺省构造函数进行初始化(数组成员不能在列表中初始化)3类型转换构造函数(单参构造函数)构造函数当其参数只有单个时它还有另一种功能:类型转换3.1在目标类型中,可以接受单个源类型对象实参的构造函数,支持从源类型到目标类型的隐式类型转换classAA(inti,intj=0,)Aa=100;3.2通过explicit关键字,可以强制这种通过构造函数实现的类型转换必须显式地进行,4析构函数4.1析构函数形式:类名()4.2特点:无返回值,无参数,且不能重载4.3何时被调用:在销毁对象时,会自动调用,且仅被调用一次(也可手调用)4.4应用:一般用于对对象在构造过程或生命周期内所申请的资源内存也可以执行其他类设计者所需要的最后使用对象之后的操作(如没有动态分配内存,非必需定义析构函数)4.5缺省析构函数,5.1如果类中没有定义析构函数,编译器会提供一个缺省析构函数5.2缺省析构函数的特性:5.2.1对基本类型的成员变量不做任何操作5.2.2对类类型的成员变量和基类子对象,会调用相关的类型的析构函数6对象的销毁过程6.1调用析构函数:执行析构函数,析构成员变量,析构基类部分6.2释放整个对象所占用的内存空间,This指针,常对象,This指针与const关键字,day13,1this指针1.1概念:指向当前对象类型的指针1.2构造函数中this代表正在“被”构建的对象的地址Aa;this=1.3在成员函数中this代表调用这个函数的对象的地址注意:Ithis是由编译器自动产生的,在类的成员函数中有效IIthis是一个常量,不允许对其赋值。III当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数IVthis指针并不是对象本身的一部分,不会影响sizeof的结果,2this指针的应用2.1区分当一个类的某个成员变量与该类构造函数的相应参数取相同标识符,在构造函数内部可以通过this指针将其区分2.2做函数的参数2.3做函数的返回值(串连调用)3常对象3.1概念:用const修饰的对象叫常对象3.2作用:被const修饰的对象的数据成员不可以被改变3.3格式:const对象名或const对象名,const*常对象指针名constAi=a;4.3.4.3从函数中返回对象4.3.4.4某些拷贝构造过程会因编译优化而被省略注:所有系统定义的构造函数,其访问控制属性均为公有(public)A;二静态成员static,7.1静态成员属于类而不属于对象/受类的访问属性限制的全局变量或函数特性:1静态成员变量不包含在对象实例中,生命期为整个程序2静态成员函数无this指针与常属性/3静态成员与其他成员一样受访问控制的限制7.2静态成员的定义与初始化静态成员只能在类的外部进行初始化Aa1a2staticinti;7.3所有该静态成员变量所属的类的对象共有静态成员7.4访问属性1静态成员函数只能访问静态成员,2非静态成员函数可以访问静态成员也可以访问静态成员3不使用对象也可以直接使用静态成员静态成员可以看成是受类的访问属性控制的全局变量或全局函数,设计模式:单例模式,day15,一单例模式:一个类只有一个实例(任务管理器),而且自行实例化并向整个系统提供这个实例(最常用,最简单的模式)二根据实例化对象的时机的不同分为两种:1饿汉式:程序启动便创建实例/2懒汉式:调用时创建三优点:1内存中只有一个实例,节约内存2避免频繁创建销毁对象,提高性能3线程安全,成员指针,day16,1成员指针:是用于对类中成员进行操作1.1定义格式:成员类型类名:*指针名=对象指针-*成员变量指针1.3与普通指针的区别:普通指针用确定对象的地址进行初始化,指向一个确定的对象成员指针用类的成员(是类的成员,而不是对象的成员)初始化(其本质是记录了特定成员变量在对象实例中的相对地址,再解引用时,根据调用对象的地址计算出成员变量的绝对地址),2成员函数指针2.1定义格式:返回类型(类名:*成员函数指针)(形参表);成员函数指针=2.2使用格式:(对象.*成员函数指针)(实参表)(对象指针-*成员函数指针)(实参表)2.3在调用成员函数指针时使用对象或对象指针的原因是传递this指针,3静态成员指针(与普通指针并没有本质区别只是多了类的防控属性)3.1定义格式类型*静态成员变量指针静态成员变量指针=静态成员函数指针(实参表),双目运算符,友元,二元操作符重载,day17,1操作符重载概念:在C+中,允许把已经定义的有一定功能的操作符进行重新定义,来完成更为细致具体的运算等功能,运算符重载和函数重载都为简单的一类多态2重载目的:c+的面向对象的程序设计需要依赖类的特性,运算符重载的使用使类类在使用普通运算符时变的更加容易3双目操作符的表达式:L#Roperator#a+b重载形式(成员函数):L.operator#(R)t1.operator+(TIME,成员函数形式:classLEFTLEFT全局函数形式:LEFT5.4注意事项5.4.1友元关系是单向的5.4.2友元关系不能被传递5.4.3友元关系不能被继承,自增运算符,输入输出运算符,一元操作符重载,day18,1单目操作符表达式:#o/o#2重载形式2.1重载形式(成员函数):O.operator#()a.operator+(b)2.2重载形式(全局函数)::operator#(O)a+;2.3前自增减(操作数为左值,表达式的值为左值,且为操作数本身)classA/成员函数形式AAconstAoperator#(A如果重载以成员函数形式重载操作符,左操作ostream类为标准库提供且无法添加新成员,那么只能以全局形式重载该操作符,重载形式(全局函数):ostream相对于普通指针,当其离开作用域时指针本身将会释放,但指针指向的内存却因为指针的释放而无法得到释放,而造成内存泄漏。智能指针是封装了普通指针的类类型的对象,当其离开作用时自动执行的析构函数负责了对普通指针指向的内存的释放3智能指针与普通指针的比较3.1智能指针重载了“*”,“-”运算符;,3.2一个对象只能有一个指向它的智能指针,而普通指针可以是多个3.3智能指针在做赋值时是以转义的形式实现(非深/浅拷贝赋值)3.3智能指针不能用于对象的数组autoptrAa=bPointerAAoperator*A*operator-return,概念,语法,继承方式,继承,day21,访控属性,1面向对象的特征:封装,继承,多态2封装:也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏3继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展(类之间是is-a(kindof)的关系)4基类与派生类:4.1基类(父类):被继承的类4.2派生类(子类):继承数据的类5继承的作用:,5.1代码复用5.2功能扩展6语法class子类:继承方式1基类1,继承方式2基类2;7继承方式公有继承:public私有继承:private保护继承:protected8公有继承的基本特点,8.1访问属性:a子类中可以直接访问基类的所有公有和保护成员b子类中不可以直接访问基类的私有成员8.2名字隐藏:如果子类中有与基类中的公有或保护名字相同的成员,那么子类的名字或隐藏所有基类中的同名成员如果使用子类或通过子类访问基类的被子类隐藏的成员可以利用:进行调用9继承方式与访问控制因为子类的继承方式的不同,子类对访问基类时的访问控制属性也会发生变化,基类中公有子类保护子类私有子类-公有成员公有成员保护成员私有成员保护成员保护成员保护成员私有成员私有成员私有成员私有成员私有成员注意:1子类访问基类成员的最大的权限构造本类成员变量-执行构造代码1.4阻断继承:子类在构造基类子对象时无论如何都要调用基类中的构造函数,如果需要子类无法实例化对象或子类无法被扩展,可以将基类的构造,函数的访问属性设定为私有2.1子类析构函数隐式调用基类析构函数子类的析构函数在执行完其中的析构代码,并析构完所有的成员变量以后,会自动调用其基类的析构函数,析构该子类对象中的基类子对象(基类析构函数不会调用子类析构函数)通过基类指针析构子类对象,实际被析构的仅仅是子类对象中的基类子对象,子类的扩展部分将失去被析构的机会,极有可能形成内存泄漏2.2子类对象的析构过程执行析构代码-析构成员变量-析构基类子对象,拷贝构造函数,拷贝赋值运算符函数,继承中的操作符重载,继承中的拷贝构造与拷贝赋值,day23,1拷贝构造函数1.1如果子类未定义拷贝构造函数,拷贝过程中的子类将调用缺省的拷贝构造,其会自动调用其基类的拷贝构造函数,构造子类对象中的基类子对象1.2如果子类已定义拷贝构造函数,但未显式指明基类部分的构造方式则编译器还是选择基类的缺省构造函数构造子类对象中的基类子对象1.3如果子类已定义拷贝构造函数,也显式指明了其基类部分以拷贝方式构造则子类对象中的基类部分和扩展部分将一起被复制,2拷贝赋值运算符函数2.1如果子类未定义拷贝赋值运算符函数,在触发拷贝赋值时会调用子类的缺省拷贝赋值运算符函数,此函数会自动调用基类的拷贝赋值运算符函数,复制子类中的基类子对象2.2如果子类定义了拷贝赋值运算符函数,但没有显式调用其基类的拷贝赋值运算符函数,则子类对象中的基类子对象将得不到复制2.3如果子类定义了拷贝赋值运算符函数,且显示调用了其基类的拷贝赋值运算符函数,则子类对象中的基类部分和扩展部分一起被复制,3继承中的操作符重载在为子类提供操作符重载定义时,往往需要调用其基类针对该操作符所做的重载定义,完成部分工作方法是:通过将子类对象的指针或引用向上造型为其基类类型的指针或引用,可以迫使针对基类的操作符重载函数在针对子类的操作符重载函数中被调用4私有子类和保护子类类型的指针或引用,不能隐式转换为其基类类型的指针或引用,多重继承,内存分配,名字冲突,多重继承,day24,1多重继承:一个类可以同时继承多个父类的行为和特征功能,2多重继承的内存分布2.1多重继承中的子类会有多个基类子对象,他们依据继承的顺序由低地址到高地址进行排列2.2构造函数也是由继承的顺序依次构造子类的基类子对象2.3析构函数则与构造的顺序相反3名字冲突overloadnamehideBb;A*pa=pa-fun()在子类的多个基类中,如果在通过子类或在子类中访问多个基类中同名且并未隐藏的标识符时,发有名字冲突。可以通过作用域限定符显式指明所调用的基类标识符或用using指令来解决,钻石继承,钻石继承的问题,虚继承,钻石继承,day25,虚表,虚表指针,钻石继承:指一个子类继承自多个基类,且这些基类又继承自相同的基类,1钻石继承的问题派生多个中间子类的公共基类子对象,在继承自多个中间子类的汇聚子类对象中,存在多个实例,在汇聚子类中,或通过汇聚子类对象,访问公共基类的成员,会因继承路径的不同而导致不一致,2钻石问题的解决方式通过虚继承,可以保证公共基类子对象在汇聚子类对象中,仅存一份实例,且为多个中间子类子对象所共享,3虚继承虚继承:继承定义中包含了virtual关键字的继承关系虚基类:在虚继承体系中的通过virtual继承而来的基类3.1在继承链最末端的子类的构造函数负责构造虚基类子对象3.2虚基类的所有子类(无论直接的还是间接的)都必须在其构造函数中显式指明该虚基类子对象的构造方式及拷贝方式构造该虚基类子对象,否则编译器将选择以缺省方式构造该子对象;3.3与构造函数和拷贝构造函数的情况不同,无论是否存在虚基类,拷贝赋值运算符函数的实现没有区别,4虚表指针汇聚子类对象中的每个中间子类子对象都持有一个虚表指针,该指针指向一个被称为虚表的指针数组的中部,该数组的高地址侧存放虚函数指针,低地址侧则存放所有虚基类子对象相对于每个中间子类子对象起始地址的偏移量,多态,虚函数,函数重写,多态,day26,1多态的一般性描述:向不同的对象发送同一消息(函数调用),不同的对象在接收时会产生不同的行为(一个接口,多个方法)foo()(学校校长向社会发布9月1日开学,不同对象的响应)2多态基于系统实现角度分为两类:静态多态性(之前学过的函数重载,运算符重载,编译时决定调用哪个函数),动态多态性(运行时决定调用哪个函数),3虚函数声明形式:virtual返回类型函数名(形参表)注:3.1c+规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数(所以在派生类中的函数可以加virtual也可以不加)3.2只有类的成员函数才可以声明为虚函数3.3一个成员函数被声明为虚函数后,该类中不允许有其他与其虚函数相同签名的函数3.3一个类中,除了构造函数和静态成员函数外,任何函数都可以被声明为虚函数,4函数重写:在父子类中子类提供了一个和父类同名的虚函数子类的实现就会把父类的实现覆盖掉(是名字隐藏的一个特例)5多态:当父类型的指针(或者引用)指向子类对象时如果调用虚函数并且子类重写了这个虚函数则调用的表现是子类的否则是父类的本质上去调用哪个成员函数完全由调用者的指针或引用的实际目标对象的类型决定5.1继承是构成多态的基础5.2虚函数是构成多态的关键5.3函数重写是必备条件,有效的虚函数数覆盖需要满足如下条件该函数必须是成员函数,既不能是全局函数也不能是静态成员凼数该函数必须在基类中用virtual关键字声明为虚凼数覆盖版本与基类版本必须拥有完全相同的签名,即函数名、形参表和常属性严格一致如果基类版本返回基本类型数据,那举覆盖版本必须返回相同类型的数据如果基类版本返回类类型对象的指针或引用,那举覆盖版本可以返回其子类类型对象的指针或引用,如果基类版本带有异常说明,那举覆盖版本不能说明比基类版本抛出更多的异常无论基类版本位于基类的公有、私有还是保护部分,覆盖版本都可以出现在子类包括公有、私有和保护在内的仸何部分全局形式重载的操作符函数不能形成多态函数签名:函数名参数列表常属性函数原型:返回值函数名参数列表常属性,纯虚函数与抽象类,day27,1纯虚函数1.1一般声明形式:virtual返回类型函数名(形参列表)=0;1.2作用:纯虚函数无函数体,声明纯虚函数是在基类中为其派生类保留一个函数名字,为派生类根据需要对它进行定义1.3注意:ABfun()如果声明了虚函数,但在派生类中未对其定义,则该虚函数在派生类中仍纯虚函数,2抽像类概念:至少拥有一个纯虚函数的类称为抽象类特性:2.1无法实例化对象2.2子类如果不对基类中的全部纯虚函数提供重写,则该子类也是抽象类作用:只做为一种基本类型用作继承的类。也称为抽象基类3纯抽象类:全部由纯虚函数构成的抽象类称为纯抽象类(构造析构除外)引入原因1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。2、在很多情况下,基类本身生成对象是不合情理的,注意1纯虚函数用来规范派生类的行为,即接口包含纯虚函数的类是抽象类,抽象类不能定义实例2虚函数必须实现,如果不实现,编译器将报错,错误提示为:errorLNK*:unresolvedexternalsymbolpublic:virtualvoid_thiscallClassName:virtualFunctionName(void)“3实现了纯虚函数的子类,该纯虚函数在子类中就变成了虚函数,子类的子类即孙子类可以覆盖该虚函数,由多态方式调用的时候动态绑定。4在有动态分配堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚的。5它们必须在继承类中重新声明函数(不要后面的0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。,6抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类7Template模式8strategy模式,虚函数表和动态绑定,day28,1多态的底层实现1.1静态绑定:如果一个函数在编译时确定了函数的调用地址则称之为静态绑定/非虚1.2动态绑定:如果一个函数在运行时确定了函数的调用地址则称之为动态绑定(会增加内存空间,无法进行内联优化)2虚函数表虚函数是通过一张虚函数表(VirtualTable)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。,2.1虚函数按照其声明顺序放于表中。2.2父类的虚函数在子类的虚函数前面动态绑定:1.确定调用指针或引用的目标对象的真实类型2.从调用指针或引用的目标对象中找到虚函数表,并从虚凼数表中获得所调用虚凼数的入口地址3.根据入口地址,调用该凼数,运行时类型信息,day29,1动态类型转换dynamic_cast()在多态继承时,可用dynamic_cast将基类类型的指针或引用转换为其子类类型的指针或引用只有“需要被强制转换的指针(或引用)”指向的对象是目标类型或者目标类型的派生类型时,强制转换运算符才会成功;否则,强制转换运算符失败。如果在转换指针的类型时失败,那么经过dynamic_cast运算后得到的指针将会使空值。如果在转换引用的类型时失败,那么dynamic_cast将会抛出bad_cast类型的异常。,2typeid操作符作用:获得一个typeinfo类型对象的常引用使用形式:typeid(object)其中,object是你想获得“类型信息”的对象返回值:typeid将返回一个type_info类型的对象引用来描述object的类型信息。在type_info中定义了下面这些公有成员:booloperator=(consttype_info,当其作用于基类类型的指针或引用的目标时,若基类包含至少一个虚函数,即存在多态继承,typeid所返回类型信息将由该指针或引用的实际目标对象的类型决定,否则由该指针或引用本身的类型决定3虚析构作用:在delete一个指向子类对象的基类指针时,实际被调用的仅仅是基类的析构函数来负责析构子类对象中的基类子对象,形成内存泄漏如果将基类的析构函数声明为虚函数,则依据多态,调用的则是子类的析构函数,将子类对象的扩展部分析构,然后会自动调用基类的析构完成资源全部的释放,工厂模式,day30,一简单工厂优点1.隐藏了对象创建的细节,将产品的实例化推迟到子类中实现2.客户端基本不用关心使用的是哪个产品,只需要知道用哪个工厂就行了,、提供的类型也可以用比较便于识别的字符串。3.方便添加新的产品子类,每次只需要修改工厂类传递的类型值就行了。4.遵循了依赖倒转原则,缺点:1.要求产品子类的类型差不多,使用的方法名都相同,如果类比较多,而所有的类又必须要添加一种方法,则会是非常麻烦的事情。或者是一种类另一种类有几种方法不相同,客户端无法知道是哪一个产品子类,也就无法调用这几个不相同的方法。2.每添加一个产品子类,都必须在工厂类中添加一个判断分支,这违背了开放-封闭原则。,二工厂模式工厂模式基本与简单工厂模式差不多,上面也说了,每次添加一个产品子类都必须在工厂类中添加一个判断分支,这样违背了开放-封闭原则,因此,工厂模式就是为了解决这个问题而产生的。优点:既然每次都要判断,那我就把这些判断都生成一个工厂子类,这样,每次添加产品子类的时候,只需再添加一个工厂子类就可以了。这样就完美的遵循了开放-封闭原则缺点:,但这其实也有问题,如果产品数量足够多,要维护的量就会增加,好在一般工厂子类只用来生成产品类,只要产品子类的名称不发生变化,那么基本工厂子类就不需要修改,每次只需要修改产品子类就可以了。,三抽象工厂AbstractFactory模式关键就是将这一组对象的创建封装到一个用于创建对象的类(Factory)中,维护这样一个创建类总比维护n多相关对象的创建过程要简单的多优点1.封装了产品的创建,使得不需要知道具体是哪种产品,只需要知道是哪个工厂就行了。2.可以支持不同类型的产品,使得模式灵活性更强。3.可以非常方便的使用一族中间的不同类型的产品,缺点1.结构太过臃肿,如果产品类型比较多,或者产品族类比较多,就会非常难于管理。2.每次如果添加一组产品,那么所有的工厂类都必须添加一个方法,这样违背了开放-封闭原则。所以一般适用于产品组合产品族变化不大的情况。,观察者模式是一种行为模式,它定义了一种一对多的依赖关系,让多个观察者监听同一个主题对象,当主题对象的状态发生改变时,它将通知所有的观察者对象。MFC中的文档视图结构就是一个典型的观察者模式。当文档对象的数据改变时,它所对应的视图会得到通知,C中错误处理,异常机制,异常处理机制,day31,一错误种类:语法错误逻辑错误功能错误设计缺陷需求不符环境异常操作不当二语言的错误处理,使用标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于头文件中。2使用assert(断言)宏调用,位于头文件中,当程序出错时,就会引发一个abort()。3使用errno全局变量,由C运行时库函数提供,位于头文件中。4使用goto语句,当出错时跳转。5使用setjmp,longjmp进行异常处理。/c库,二异常一新的错误表达形式如何抛出异常throw数据;数据可以是基本类型也可以是类类型的,但不可以抛出局部对象的指针异常处理如果程序抛出异常默认的调用terminate函数终止进程需要异常进行捕获,捕获异常try可能引发异常的语句;catch(异常类型1,4函数的异常说明voidfoo();这代表可能会抛出任何异常voidfoo()throw();这代表不抛出任何异常并且这样写的情况下函数内部还抛出异常但这些异常不能被捕获voidfoo()throw(int);这代表可能会抛出int异常voidfoo()throw(int,double);这代表可能会抛出int或者double的异常注意:如果函数抛出了异常说明以外的异常类型,那么该异常将无法被捕获,并导致进程中止std:unexpected()-std:terminate()-abort(),忽略异常,不做处理,异常会向上传递fun1fun2();,类异常,标准库异常,类中的异常处理,day32,一类类型的异常1抛出类类型的异常,根据异常对象的类型分别处理,利用类类型的异常,携带更多诊断信息,以便查错2捕获异常2.1根据异常对象的类型自上至下顺序匹配,而非最优匹配,因此对子类类型异常的捕获不要放在对基类类型异常的捕获后面2.2在捕获异常中最好使用引用类型来接收异常对象,提高性能避免新异常2.3为每一种异常定义相对应的异常类型,2.4推荐以匿名临时对象的形式抛出异常2.5建议从标准库异常中派生自己的异常2.6异常说明也是函数原型的一部分2.7抛出标准库异常,或标准库异常的子类异常,允许用户以与标准库一致的方式捕获该异常,流的概念,流类库,四个流类全局对象,i/0流,day33,1流:c+程序把输入输出看作字节流流可以表示程序从内存传送到某个载体或设备中即输出流也可以表示数据从某个载体或设备传递到内存缓冲区变量中即输入流2i/o即数据的输入和输出2.1标准i/0:对标准输入设备键盘和标准输出设备显示器的输入输出2.2文件i/0:对磁盘上的文件的输入输出2.3串i/0:对内存中指定的字符串存储空间进行的输入输出3i/0类库c+提供了对数据输入输出的类库,根基类:ios根基类直接派生类:1输入流类istream2输出流类ostream3文件流类fstreambasse4字符串流基类strstreambase5缓冲流类streambuf,4类库中四个全局流对象(头文件)cin标准输入流对象,键盘带缓冲区cout标准输出流对象,显示器带缓冲区clog标准错误输出流对象显示器带缓冲区cerr标准错误输出流对象显示器无缓冲区(一旦出错马上显示)5基本读写操作5.1:字符流目的地字符流内容cout为ostream类的全局对象,其重载了write(),标志位,成员函数,格式化i/o,day34,一格式化I/Oios类中提供了格式化控制格式化输入输出的两种方式1第一种方式:标志位与成员函数1.1控制格式的标志位定义为公有无名的枚举类型如下enumskipws=0 x0001,/跳过输入中的空白字符left=0 x0002,/左对齐right=0 x0004,/右对齐internal=0 x0008,/数值右对齐,符号左对齐dec=0 x0010,/十进制处理,oct=0 x0020,/八进制处理hex=0 x0040,/十六进制处理showbase=0 x0080,/前导0或0 xshowpoint=0 x0100,/显示小数点和尾数0uppercase=0 x0200/输出十六进制,用大写字母showpos=0 x0400,/输出正数时,加”+”号scientific=0 x0800,/科学计数方式输出浮点数fixed=0 x1000,/定点数方式输出实数unitbuf=0 x2000,/插入后,立即刷新流的缓冲区stdio=0 x4000/插入后,立即刷新stdout和stderr,注:枚举值代表对应两字节中的一位的值,可以用位或运算进行组合成为一个长整型数1.1.1staticconstlongadjustfieldios:adjustfield清除对齐标志1.1.2staticconstlongbasefield;ios:basefield清除进制标志1.1.3staticconstlongfloatfieldios:floatfield清除浮点标志例:cout.setf(ios:hex,ios:basefield),1.2格式化设置标志的成员函数intios:precision(int);设置浮点精度,返回原精度intios:precision(void)const;获取浮点精度intios:width(int);设置显示域宽,返回原域宽intios:width(void)const;获取显示域宽charios:fill(char);设置填充字符,返回原字符charios:fill(void)const;获取填充字符longios:flags(long);设置格式标志,返回原标志longios:flags(void)const;获取格式标志,longios:setf(long);添加格式标志位,返回原标志longios:setf(long,long);添加格式标志位,返回原标志先用第二个参数将互斥域清零longios:unsetf(long);清除格式标志位,返回原标志注:除设置域宽状态外的其他i/o流格式的改变都为持久性的2第二种方式:控制符(操作子,头文件为iomanip.h)控制符为iomanip中的对象可以插入到数据流被操作,操作符作用输入/输出dec十进制显示i/ohex十六进制显示i/ooct八进制显示i/oSetbase(intn)设置数据基数(默认10进制)i/ows提取空白字符iends插入空字符oendl换行符,刷新ostream缓冲区oflush刷新ostream缓冲区o,resetiosflags(long)清除参数指定的标志位i/osetiosflags(long)设置参数指定的标志位i/osetfill(char)设置填充字符osetprecision(int)设置浮点数的有效数位osetw(int)设置输出数据的宽度o2.1自定义流控制符2.1.1无参流控制符流类型2.1.2带参流控制符,流类型,文本文件的打开与关闭,打开方式,文本文件的读写,文件i/o操作,day35,一处理文件相关的数据分为两类:二进制文件与文本文件1文本文件:由字符序列组成(ASCII文件)单位为字符2二进制文件:存取的单位为字节二文本文件的i/o操作#includeifstream输入文件流对象名ifstream(constchar*filename,openmodemode);ofstream输出文件流对象名ofstream(constchar*filename,openmodemode);fstream输入输出文件流对象名,fstream(constchar*filename,openmodemode);打开i/o流1voidopen(constchar*filename,openmodemode,缺省);使用文件流对象名.open(filename,mode)文件访问方式常量表(ios中定义的公有枚举常量)方式名称作用in读取文件(不在则失败,存在不清空)ifstream的缺省out写入文件(不在则创建,存在则清空)ostream的缺省app文件尾追加(不在则创建,存在不清空),ate打开文件时,文件置于文件尾trunc清除文件内容或创建新文件binary以二进制方式打开,缺省时为文本nocreate打开已有文件,不创建新文件noreplace不替换文件内容,若文件在在且未设app,ate则失败in|out以读写方式打开out|binary以二进制文件方式打开(向文件输出)in|binary以二进制文件方式打开(向文件输入),2文件流对象名.close();/文件流对象名就是内部文件名。3对文件读取与写入操作3.1调用运算符写入与输入文件3.2使用成员函数getlineputline读写3.3使用成员函数get()put()读写三二进制i/o1istream,将要读入字符串的地址,num为要读写字符的个数,读写指针位置函数,其他函数,i/o随机读写,day36,一设置读/写指针位置1读写位置成员函数原型1.1读取istreamios:beg(值为0)文件开头ios:cur(值为1)当前位置ios:en

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论