C++ 笔记35页文档格式.docx

上传人:b****3 文档编号:6776628 上传时间:2023-05-07 格式:DOCX 页数:53 大小:53.70KB
下载 相关 举报
C++ 笔记35页文档格式.docx_第1页
第1页 / 共53页
C++ 笔记35页文档格式.docx_第2页
第2页 / 共53页
C++ 笔记35页文档格式.docx_第3页
第3页 / 共53页
C++ 笔记35页文档格式.docx_第4页
第4页 / 共53页
C++ 笔记35页文档格式.docx_第5页
第5页 / 共53页
C++ 笔记35页文档格式.docx_第6页
第6页 / 共53页
C++ 笔记35页文档格式.docx_第7页
第7页 / 共53页
C++ 笔记35页文档格式.docx_第8页
第8页 / 共53页
C++ 笔记35页文档格式.docx_第9页
第9页 / 共53页
C++ 笔记35页文档格式.docx_第10页
第10页 / 共53页
C++ 笔记35页文档格式.docx_第11页
第11页 / 共53页
C++ 笔记35页文档格式.docx_第12页
第12页 / 共53页
C++ 笔记35页文档格式.docx_第13页
第13页 / 共53页
C++ 笔记35页文档格式.docx_第14页
第14页 / 共53页
C++ 笔记35页文档格式.docx_第15页
第15页 / 共53页
C++ 笔记35页文档格式.docx_第16页
第16页 / 共53页
C++ 笔记35页文档格式.docx_第17页
第17页 / 共53页
C++ 笔记35页文档格式.docx_第18页
第18页 / 共53页
C++ 笔记35页文档格式.docx_第19页
第19页 / 共53页
C++ 笔记35页文档格式.docx_第20页
第20页 / 共53页
亲,该文档总共53页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

C++ 笔记35页文档格式.docx

《C++ 笔记35页文档格式.docx》由会员分享,可在线阅读,更多相关《C++ 笔记35页文档格式.docx(53页珍藏版)》请在冰点文库上搜索。

C++ 笔记35页文档格式.docx

4、在覆盖定义基类成员函数时,调用基类中的该函数版本需用作用域运算符,否则实际调用的是该函数自身,会引起无穷递归。

例:

BaseClass:

:

function();

5、继承类型:

a、public继承:

基类的public成员成为派生类的public成员,protected成员成为派生类的protected成员,private成员则隐藏。

b、protected继承:

基类的public成员和protected成员成为派生类的protected成员,private成员在派生类中不可见,隐藏。

c、private继承:

基类的public成员和protected成员成为派生类的private成员,private成员在派生类中不可见,隐藏。

private和protected继承不是“is-a”(“是”)的关系。

继承默认是private的。

6、类的构造是从类层次的最根处开始,而在每一层,首先会调用基类构造函数,然后调用成员对象构造函数。

析构严格按照相反的顺序。

注意:

基类的构造在成员对象构造之前。

构造函数调用的次序完全不受构造函数的初始化列表中次序的影响。

7、把派生类对象赋给基类对象,然后试图在该基类对象中引用只在派生类中才有的成员是个语法错误。

8、多重继承:

用virtual继承时,派生类中只出现一个子对象,可解决重复子对象的问题。

9、java和C++的区别:

java是纯粹的(pure)的面向对象语言,而C++是一种混合的(hybrid)语言。

java是基于对象的继承层次结构模型,所有类都是在一个单一的继承层次结构中派生的,有一个基类为根(Object)。

10、非自动继承的函数:

构造函数,析构函数,赋值函数(operator=)因为它完成类似于构造函数的功能。

11、静态成员函数不可以是虚函数。

12、将私有继承成员公有化:

在派生类中的public部分声明其名字,例:

public:

usingbaseClass:

memberName;

//给出一个重载函数的名字,将使基类中所有它的重载版本公有化

13、除赋值运算符外,其余运算符的重载都可以自动地继承到派生类中。

14、类中编译器可自动生成的四个函数:

构造函数,拷贝构造函数,析构函数和赋值运算符(operator=)。

15、确定使用组合(composition)还是继承(inheritance)的方法是:

是否需要从新类向上类型转换;

确定是否需必须使用多重继承的方法是:

是否需要将一个对象指针向上类型转换成两个不同的基类类型。

16、多重继承分两种:

接口继承(interfaceinheritance)和实现继承(implementationinheritance)。

接口继承仅仅是在派生类接口中加入成员函数的声明,C++不支持这样用法,C++仅提供了实现继承,接口继承需用特别方的法实现。

17、多重继承的一个共同用途包括使用混入类(mixin),混入类的存在是为了通过继承来增加其他类的功能。

(卷二P359)

18、“菱形”继承:

通过使用虚基类(不是抽象基类)来消除重复子对象,使用virtual关键字继承(virtualpublic)。

19、基类必须有虚析构函数,虽然没有编译器也可以编译通过。

20、编译器对多重继承的实现原则:

一个带有多重继承的派生对象必须表现出它好像有多个VPTR,它的每个含有虚函数的直接基类都有一个。

21、由于菱形继承中基类构造的二义性,规定:

最高层派生类(继承树最叶子端的类)必须初始化一个虚基类(卷二P367)。

而中间层次的派生类对基类的初始化将被忽略。

22、多重继承名字查找问题:

若基类中含有同名的函数,派生类调用该函数将产生编译错误。

消除方法:

用基类名来限定函数的调用,例:

在派生类中:

usingbaseClass:

function;

//注意声明函数名即可,不需括号

另外,基类成员在派生类中的作用域规则:

在继承层次结构中,离派生类越近的上层类,其成员在派生类中的优先级越高。

23、多重继承的优势只与虚基类一同存在。

虚函数和多态:

(多态--polymorphism,封装--encapsulation)

1、一旦一个函数被声明为虚函数,即使重新定义时没有被声明为虚函数,它在该点之后的继承层次结构中仍都是虚函数。

2、抽象类:

有一个或多个纯虚函数的类,也称抽象基类。

其唯一的用途是为其他类提供合适的基类,其他类可从它这继承或实现接口。

抽象类仍然可以提供虚函数的实现。

3、若抽象基类的派生类没有提供抽象基类中纯虚函数的定义,则该派生类仍然是抽象类,因为继承了基类的纯虚函数。

4、多态性:

通过继承相关的不同类,他们的对象能够对同一个函数调用作出不同的响应。

5、多态性是通过虚函数实现的。

通过基类型的指针或引用请求调用虚函数时,C++会在与对象关联的派生类中正确地选择重定义的函数。

注意只能是基类型的指针(BaseClass*)或基类型引用(BaseClass&

),不能是传值调用(BaseClass),防止对象切片。

6、不能实例化抽象类的对象,但可以声明引用抽象类的指针,指向具体类的对象后,这种指针使派生类对象具有多态操作能力。

7、给基类提供一个虚析构函数,这样删除该类的派生类时,自动删除基类部分。

8、将构造函数定义成虚函数是个语法错误,构造函数不能是虚函数。

但一般需要将基类的析构函数定义成虚函数。

9、C++多态的实现原理:

多态是通过复杂的数据结构实现的,涉及三层指针。

第一层:

C++编译时,给每个含有虚函数的类建立一个虚函数表(VTABLE),该表中,对每个虚函数都有函数指针指向,而若为纯虚函数,则函数指针设置为0。

因此vtable中有0时为抽象类(abstractclass),没有0时为具体类(concreteclass)。

第二层:

对每个实例化的带虚函数的类对象,编译器在对象前面添加指向该类vtable的指针(VPTR)。

第三层:

接受虚函数调用的对象句柄(指针或引用)。

多态实现步骤:

a将基类指针或引用指向具体类对象;

b通过基类句柄获得具体类对象,该对象以vtable指针开始;

c复引用vtable指针获得该具体类的vtable;

d搜索vtable找到需调用的函数指针;

e复引用函数指针,调用该函数。

10、虚函数增强了类型概念,它使类概念不仅仅只是在结构内隐藏地封装代码。

它是面向对象程序设计的核心所在。

11、动态绑定(dynamicbinding)(也称晚绑定,运行时绑定)只对virtual函数起作用,且只在使用含有virtual函数的基类的指针或引用时发生。

不能是基类对象的一个原因是传值会导致对象切片。

12、创造C语言是为了代替汇编以实现操作系统,发明C++的主要原因之一是让C程序员的工作效率更高。

13、对只存在于派生类中的函数做虚函数调用,会产生编译错误。

14、编译器不允许我们在派生类中改变基类中虚函数的返回值,若不是虚函数,则是可以的,但基类中所有同名函数在派生类中都自动隐藏。

另外,如果基类中返回值是某基类对象,在派生类中将返回值改成该基类的派生类,则是可以的。

15、虚机制在构造函数和析构函数中均失效。

16、纯虚析构函数:

唯一的效果是阻止基类的实例化。

定义纯虚析构函数,我们必须提供一个函数体。

17、单根继承(single-rootedhierarchy),也称基于对象的继承(object-basedhierarchy):

所有类都直接或间接地从一个公共基类中继承出来。

java等除C++外的面向对象语言都是用这种体系。

18、向下类型转换(downcasting):

C++提供了一个特殊的称为dynamic_cast的显式类型转换(explicitcast)。

但使用dynamic_cast时,对象必须含有虚函数,因为它使用到了VTABLE中的信息。

模板介绍

1、有三种代码重用的方法:

a、C方法(繁琐,已发生错误,摒弃);

b、Smalltalk方法:

通过继承来实现代码重用,每个容器类都包含通用的基类Object的项目,java中的方法。

c、C++中的模板:

模板实现了参数化类型(parameterizedtype)的概念。

2、模板语法:

template关键字告诉编译器,随后的类定义将操作一个或多个未指明的类型。

template<

classT,intsize>

.......

template<

...>

之后的任何东西都意味着编译器在当时不为它分配存储空间,而是一直处于等待状态直到被一个模板实例告知。

编译器和连接器中有机制能去掉同一模板的多重定义。

3、懒惰初始化(lazyinitialization):

数据成员不在构造函数中初始化,而是推迟到第一次访问时初始化。

若创建大量的对象,但不访问每一个对象,为节省存储,可以使用懒惰初始化。

4、容器所有权问题:

当容器中包含指向对象的指针,而这些指针很可能用于程序的其他地方,此时需考虑所有权问题。

处理所有权问题的最好方法是由客户程序员来选择,通常通过构造函数的一个参数来实现。

constructor(....,boolowner);

5、迭代器(iterator):

迭代器是一个对象,它在其它对象的容器上遍历,每次选择其中一个,不需要提供对这个容器的实现的直接访问。

迭代器提供了一种访问元素的标准方法,不管容器是否提供了直接访问元素的方法。

迭代器是一个“灵巧指针”,其关键是:

从一个容器元素移动到下一个元素的复杂过程被抽象为像一个指针一样。

C++的异常处理机制

1、传统的错误处理:

assert,errno,perror,signal信号处理系统,非局部跳转(setjmp(),longjmp())。

问题:

C中的信号处理方法和函数setjmp/longjmp并不调用析构函数,所以对象不会被正确的清理。

2、异常匹配:

匹配一个异常并不要求异常与其处理器之间完全相关,一个对象或是指向派生类对象的引用都会与其基类处理器匹配。

匹配过程中不会将一种异常类型自动转换成另一种异常类型,转换构造函数在异常匹配中失效。

3、全能捕获:

catch(...){}

4、重新抛出异常:

在一个异常处理器内部,可以使用不带参数的throw语句重新抛出异常,且这个异常的所有信息都会自动保留,以传给更高层的异常处理器。

5、异常没有被捕获,则库函数terminate()函数会自动调用。

terminate默认调用C库函数abort(),abort被调用时,程序不会执行正常的终止函数,全局对象和静态对象的析构函数不会执行。

当局部对象的析构函数或全局,静态变量的构造或析构函数抛出异常时,也会调用terminate。

set_terminate()可设置自己的terminate函数。

6、C++异常处理确保当程序执行流程离开一个作用域的时候,对于在这个作用域中所有由构造函数建立起来的对象,它们的析构函数一定会被调用。

当在构造函数中发生异常时,析构函数将不会调用,因而申请的资源无法释放,并且后面的一系列的对象也将不能继续构造函数。

将出现“悬挂”指针(nakedpointer)。

解决办法:

a、在构造函数中捕获异常;

b、使用模板来封装指针(卷二P13)。

7、异常规格说明语法:

可能抛出的所有可能的异常类型都应该写在throw后的括号中。

voidf()throw(exp1,exp2,,,);

传统的函数声明voidf()意味着可以跑出任何类型的异常;

而voidf()thow()则表示不会抛出任何异常。

8、如果函数所抛出的异常没有列在异常规格说明的异常集中,则会调用unexpected()函数。

set_unexpected可设置自己的unexpected函数,该函数不能有参数和返回值。

9、异常规格说明和继承:

派生类中覆盖函数不能在异常规格说明列表中添加其他异常,可改为父类中异常类型的派生类型。

10、避免在模板类中使用异常规格说明,因为无法预料模板参数类所抛出的异常类型。

11、异常安全:

栈容器中出栈(pop)和删除栈顶元素(top)分开实现的原因是为了保证异常安全。

若将两个动作放在一个函数中,当为了得到返回值而调用拷贝构造函数时,拷贝构造函数在最后一行抛出异常,此时,栈顶元素丢失了,而返回却失败。

因此将改变栈状态和返回值两个动作分开实现,这也遵守了高内聚设计原则--每个函数只做一件事情。

异常安全代码能使对象保持状态的一致性而且避免资源泄漏。

12、通过引用而不是值来捕获异常:

a、当异常对象传递到异常处理器中的时候,避免进行不必要的对象拷贝;

b、当派生类被当做基类对象捕获时,避免对象切割。

13、不能有异常从析构函数中抛出,因此在抛出其他异常的过程中析构函数会被调用。

否则会导致调用terminate函数。

析构函数内若可能产生异常,则必须析构函数自行处理。

14、当异常被抛出时,将造成相当多的运行时开销。

深入理解字符串

1、字符串的内部实现:

C语言中字符串就是字符型数组,且总是以二进制零结束;

C++string与C字符串最大的不同是它隐藏了字符串的物理表示。

极大的减少了C语言中3种常见错误:

数组越界;

野指针访问;

释放数组空间后产生悬挂指针。

2、创建并初始化C++字符串的方法:

stringsBlank;

stringstr("

hello"

);

stringstrcopy="

thisis"

"

acopy"

;

stringcopy(str);

strings1(str,0,4);

strings2=strcopy+"

abc"

+str.substr(1,2);

strings3(str.begin(),str.end());

strings4(5,‘a’);

//初始化为a字符重复5次,即“aaaaa”,第二个参数只能是char字符

3、字符串操作函数:

追加:

append();

插入:

insert();

长度:

size();

当前分配的存储空间大小:

capacity();

替换:

replace();

预留空间:

reserve();

查找:

find()、find_first_of()、find_last_of()、find_first_not_of()、find_last_not_of()、rfind()//逆向查找,将字符串反过来;

string类中没有改变字符串大小写的函数,可借用C语言的库函数toupper()和tolower()实现;

删除:

erase();

返回constchar*:

c_str()//不可作非const参数使用;

字符串的运算符中,没有逻辑与或非(&

&

||,!

)运算符,也没有逐位与或非(&

|,~,^)运算符;

比较:

常见的比较运算符(>

<

==等);

compare();

交换:

swap();

C++字符串类中提供一种S[n]表示法的替代方法:

at(n)函数。

若下标越界,at函数会抛出out_of_range类型异常,多用。

4、string类基于basic_string<

classT>

模板实现

5、宽字符(wchar_t):

两字节(16位)。

输入输出流

1、输入输出流:

istream对象,ostream对象,iostream对象;

文件输入输出流:

ifstream对象,ofstream对象,fstream对象;

string类输入输出流:

istringstream对象,ostringstream对象,stringstream对象。

以上流类实际上都是模板的特化(templatespecialization)。

2、流插入符(inserter):

<

提取符(extractor):

>

3、按行读取流:

成员函数get();

成员函数getline();

定义在<

string>

中的全局函数getline()。

前两个函数都有三个参数:

指向字符缓冲区的指针;

缓冲区大小;

结束字符(默认为'

\n'

)。

遇到结束字符时,均会在缓冲区

末尾存储一个零。

两者区别:

get不读取结束符,而getline读取结束符但不放入缓冲区。

中的getline为全局函数,两个非默认参数:

输入流对象和string对象。

读取界定符(delimiter默认'

)并丢弃。

数据存入

string对象中,故不必担心缓冲区溢出。

4、处理流错误:

获取状态:

eofbit置位--eof()返回真,此时failbit也会置位;

failbit或badbit置位--fail()返回真;

badbit置位--bad()返回真;

goodbit置位--good()返回真。

clear()清空流状态标志位,且置gootbit位。

setstate()设置标志位,clear()也可设置标志位。

5、文件打开模式:

ios:

in;

out;

app;

ate(打开一个已存在的文件输入/输出,并将指针移至文件尾);

trunc;

binary(使用read()或write()的时候应该以二进制打开,因为这些函数以字节为单位操作;

若要使用流指针定位命令也应以二进制打开)。

6、每个输入输出流对象都包含一个指向streambuf的指针。

每个输入输出流对象都有一个成员函数rdbuf(),用来访问streambuf,它返回一个指向对象streambuf的指针。

cout<

in.rdbuf();

7、输入输出流中定位:

a、绝对定位:

tellp(),tellg()获取当前位置,seekp(),seekg()定位;

b、相对定位:

重载版本的seekp()和seekg()函数,两个参数:

第一个为移动的字符数目,第二个为移动方向(ios:

beg,ios:

cur,ios:

end)

8、创建一个既能读文件又能写文件的方法:

法一:

fstreamio;

法二:

ifstreamin("

filename"

,ios:

in|ios:

out);

ostreamout(in.rdbuf);

9、输出字符串流:

成员函数str()可将输出流格式化为string对象,每次调用str()都会返回一个新的string对象。

10、输出流的格式化:

格式化标志;

格式化域;

操纵算子(cin>

ws可吃掉空格)。

通用容器(看得很粗糙)

1、对容器中元素灵活访问的方法是使用迭代器:

迭代器是一个对象,它作为一个类也提供了一个抽象层,因此可以将容器的内部实现细节与用来访问容器的代码分隔开来。

通过迭代器,容器可被看作一个序列,迭代器遍历该序列而无需知道其基本结构。

2、STL编程指南网站:

3、C++中容器分三类:

a序列容器:

仅将元素线性的组织起来,是最基本的容器类型,如vector、list、deque等;

b容器适配器:

在基本线性序列上添加一些特殊的属性,如stack、queue、priority_queue等;

c关联式容器:

基于关键字来组织元素,如set、map、multiset、multimap等。

4、标准库中所有的容器都持有存入对象的拷贝,所以这些对象必须是可拷贝构造(具有一个可访问的拷贝构造函数)和可赋值(具有一个可访问的赋值操作符)的。

10、当容器中持有的是对象时,容器被销毁时,其持有的对象也会被销毁,而若持有的是指针,指针不会被销毁,需要程序员自行销毁。

6、使用容器一般方式:

typedefstd:

容器名<

对象类型>

Container;

//这样方便更换容器类型,仅将容器名该一下即可

typedefContainer:

iteratorIter;

//简化迭代器的写法

typedefContianer:

const_iterator;

//这种const(常)容器中的迭代器不允许反向修改所指向的容器中的元素

reverse_iterator;

//这种可逆容器中的迭代器从序列后向前遍历

const_reverse_iterator;

//可逆const容器产生的迭代器

7、所有容器都有begin()和end()成员函数,用来产生选择序列开始端和超越末尾端的迭代器。

可逆容器则拥有成员函数rbegin()和rend()。

如果容器是const的,则均产生const型的迭代器。

8、判断迭代器是否到达尾部使用不等于end(!

=),不要使用<

或<

=,只有!

=和==测试方式有效。

通常将循环写成如下形式:

for(Iteri=ContianerObject.begin();

i!

=ContianerObject.end();

i++)

9、通过迭代器访问容器中元素的方法:

使用"

*"

解析迭代器引用,*i返回容器中持有的任何东西,同时迭代器还可以反向给非常容器中元素赋值,如*i=*i+1

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2