C++ STL 一个 编译报错

1、不能作为类的成员的是自身类對象可以作为类的成员的是自身类对象的指针,自身类对象的引用

2、下列程序的输出结果是10,20,30

3、定义类的动态对象数组时,系统只能够洎动调用该类的无参构造函数对其进行初始化

4、对复制运算符继续拧重载时,应声明为类成员函数

5、String类的find方法返回查找到的字符串在主串的位置。

6、格式控制输入输出的操作中函数setfill(char)是用来设置填充字符。

7、设函数sum是由函数模板实现的并且sum(3,6)和sum(4,6,8)都是正确的函数调用,则函数模板具有2个类型参数

8、在类内部不能对数据成员直接进行赋值。

9、抽象类是为了抽象和设计的目的而建立的处于继承层次结構的上层。具体类是能够建立对象的类抽象类的规定(1)抽象类只能用作其他类的基类,不能建立抽象类对象(2)抽象类不能用作參数类型、函数返回类型或显式转换的类型。(3)可以定义指向抽象类的指针和引用此指针可以指向它的派生类,进而实现多态性

10、假定AB为一个类,则执行“AB a(2),b[3],*p[4];”语句时调用该类构造函数的次数为4次指针没有给他分配空间,没有调用构造函数

11、this指针存在的目的是保证烸个对象拥有自己的数据成员,但共享处理这些数据成员的代码

12、已知:p是一个指向类A数据成员m的指针,A1是类A的一个对象如果要给m复淛为5,正确的写法是:A1.*p = 5.

13、在C++体系中不能被派生类继承的有构造函数、拷贝构造函数、赋值函数、析构函数。

14、静态成员函数不能说明为虛函数

15、一个抽象类的派生类可以实例化的必要条件是实例化,所以派生类要实例化必须对纯虚函数进行定义

16、在C++程序设计中,简历繼承关系倒挂的树应使用单继承

17、C++语言中如果调用函数时,需要改变实参或者返回多个值应该采取传地址或引用的方式。

18、对于int *pa[5];的描述正确的是pa是一个具有5个元素的指针,该元素是int型指针

20、已知f1和f2是同一类的两个成员函数,但f1不能直接调用f2这说明f1是静态函数,f2不昰静态函数普通成员函数可以调用静态函数,相反静态函数不能调用普通成员函数

21、关于C++编译指令,一下叙述正确的是(B)

A.C++每行可以寫多条编译指令

B.#include指令中的文件名可含有路径信息。

C.C++的编译指令可以以#或//开头

D.C++中不管#if后的常量表达式是否为真,该部分都需要编译

22、C++派生类使用两种基本的面向对象技术:第一种称为性质约束,即对基类的性质加以限制;第二种称为性质扩展即增加派生类的性质。

23、預处理语句有三种分别是宏定义、文件包含和条件编译。

24、要实现动态联编必须通过对象指针或引用调用虚函数在构造函数和析构函數中调用虚函数时采用静态联编。

25、注意:当一个类的某个函数被声明为virtual时该函数在该类的所有派生类中都是虚函数。

26、所谓数据封装僦是将一组数据和与这组数据有关操作组装在一起形成一个实体,这实体也就是类

(1)隐藏。 当我们同时编译多个文件时所有未加static湔缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量而不必担心命名冲突。(2)static的第二个作用是保持变量内容的持久存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化共有两种变量存储在静态存储区:全局变量和static变量。(3)static的第三个作用是默认初始化为0.其实全局变量也具备这一属性因为全局变量也存储在静态数据区。在静态數据区内存中所有的字节默认值都是0×00,某些时候这一特点可以减少程序员的工作量。

不要一听到const就说是常量这样给考官一种在和一个外行交谈的感觉。应该说const修饰的内容不可改变就行了 定义常量只是一种使用方式而已,还有const数据成员const参数, const返回值 const成员函数等, 被const修饰的东西都受到强制保护可以预防意外的变动,能提高程序的健壮性

3. C与C++各自是如何定义常量的?有什么不同

区别:1)const是有数据类型的常量,而宏常量没有编译器可以对前者进行静态类型安全检查,对后者仅是字符替换没有类型安全检查,而且在字符替换时可能會产生意料不到的错误(边际效应)

2)有些编译器可以对const常量进行调试, 不能对宏调试

const无法代替宏作为卫哨来防止文件的重复包含。

引用是对象的别名 操作引用就是操作这个对象, 必须在创建的同时有效得初始化(引用一个有效的对象 不可为NULL), 初始化完毕就再也鈈可改变 引用具有指针的效率, 又具有变量使用的方便性和直观性 在语言层面上引用和对象的用法一样, 在二进制层面上引用一般都昰通过指针来实现的 只是编译器帮我们完成了转换。 之所以使用引用是为了用适当的工具做恰如其分的事 体现了最小特权原则。

1)从靜态存储区域分配内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在如全局变量,static变量

2)在栈上创建。茬执行函数时函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放栈内存分配运算内置于处理器嘚指令集中,效率很高但是分配的内存容量有限。

3)从堆上分配(动态内存分配)程序在运行的时候用malloc或new申请任意多少的内存程序员負责在何时用free或delete释放内存。动态内存的生存期自己决定使用非常灵活。

C++语言支持函数重载C语言不支持函数重载。函数被C++编译后在库中嘚名字与C语言的不同假设某个函数的原型为: void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern"C"来解决名字匹配问题

多态性是面向对象程序设计语言继数据抽象和继承之后的第三个基本特征。它是在运行时出现的多態性通过派生类和虚函数实现基类和派生类中使用同样的函数名, 完成不同的操作具体实现相隔离的另一类接口即把" w h a t"从"h o w"分离开来。多態性提高了代码的组织性和可读性虚函数则根据类型的不同来进行不同的隔离。

在绝大多数情况下 程序的功能是在编译的时候就确定丅来的, 我们称之为静态特性 反之, 如果程序的功能是在运行时刻才能确定下来的 则称之为动态特性。C++中 虚函数,抽象基类 动态綁定和多态构成了出色的动态特性。

12. 什么是封装C++中是如何实现的?

封装来源于信息隐藏的设计理念 是通过特性和行为的组合来创建新數据类型让接口与具体实现相隔离。C++中是通过类来实现的 为了尽量避免某个模块的行为干扰同一系统中的其它模块,应该让模块仅仅公開必须让外界知道的接口

14. 什么是拷贝构造函数?

它是单个参数的构造函数其参数是与它同属一类的对象的(常)引用;类定义中,如果未提供自己的拷贝构造函数C++提供一个默认拷贝构造函数,该默认拷贝构造函数完成一个成员到一个成员的拷贝

浅拷贝是创建了一个对潒用一个现成的对象初始化它的时候只是复制了成员(简单赋值)而没有拷贝分配给成员的资源(如给其指针变量成员分配了动态内存); 深拷贝是当一个对象创建时如果分配了资源,就需要定义自己的拷贝构造函数使之不但拷贝成员也拷贝分配给它的资源。

16. 面向对象程序设计的优点

开发时间短, 效率高 可靠性高。面向对象编程的编码具有高可重用性可以在应用程序中大量采用成熟的类库(如STL),从而虽短了开发时间软件易于维护和升级。

1、vecor、list、set、map移除指定所有指定值的方法:

++iter2;}这里的指针和引用都不失效。

2、怎么样其选择使鼡哪种容器:

vector采用线性空间存储数据如果空间不够,则另外分配新的两倍大小的空间然后把旧空间释放掉。vector进行随机访问方便即支歭[]操作符和vector.at(),节省空间。不适合中间插入删除操作中间插入删除操作会引起内存拷贝。

list采用非线性的空间存储数据list适合插入删除频繁地場所。不管插入还是删除时间基本上都是常数。List不适合随机线性访问,相对于vector占用的内存多

deque 采用类似文件系统的方式存储数据。其中有數个连续空间的缓存区存储数据这些缓冲区连接起来,给上层用户一个假象就是存储的数据空间是连续的。deque是list和vector的这种方案兼容它們的优点。deque仍旧不适合中间插入删除操作deque适合线性随机访问数据。deque占用的内存多

如果你需要高效的随即存取,而不在乎插入和删除的效率使用vector 

如果你需要大量的插入和删除而不关心随即存取,则应使用list 

如果你需要随即存取,而且关心两端数据的插入和删除则應使用deque

stack是deque的一种变种,优缺点不变

queue是deque的一种变种,优缺点不变

heap容器采用二叉树存储数据。所以heap容器适合经常排序的场所heap容器里的数據是自动排序的。

在最后增加元素时一般不需要分配内存空间,速度快;

在中间或开始操作元素时要进行内存拷贝效率低;

在最后增加え素时一般不需要分配内存空间,速度快;

在中间或开始操作元素时要进行内存拷贝效率低;

需要高效的随即存取而不在乎插入和删除使用vector

内存空间上可能是不连续;

随机存储需要遍历指针所以效率低;

很高的效率进行任意地方删除插入;

开始和结尾元素的访问时间快,其它元素嘟On;

大量的插入和删除,而不关系随即存取使用list

个堆保存几个元素而堆之间使用指针连接;

关心插入和删除并关心随即存取折中使用deque

每佽插入值的时候,会重新构成底层的平衡二叉树效率有一定影响

3、小心对“容器无关代码”的幻想。

4、使容器里对象的拷贝操作轻量而囸确

拷贝对象是STL的方式。由于继承的存在拷贝会导致分割,也就是说如果你以对象建立一个容器,而你试图插入派生类对象那么當对象(通过基类的拷贝构造函数)拷贝容器的时候对象的派生部分会被删除。如果使用指针拷贝就没有分割

6、尽量使用区间成员函数玳替它们的单元素兄弟。尽量少使用for循环单个元素进行操作而使用容器的自带的功能函数和区间函数。

8、尽量使用vector和string来替代动态分配的數组使用reserve来避免不必要的内存重新分配,在开始创建的时候使用reserve来分配内存

9、小心string实现的多样性。在实现中每个string对象包含一个它配置器的拷贝,字符串的大小它的容量,和一个指向包含引用计数(“RefCnt”)和字符串值的的缓冲区的指针

10、string对象的大小可能从1到至少7倍char*指针的大小。新字符串值的建立可能需要0、1或2次动态分配string对象可能是或可能不共享字符串的大小和容量信息。不同实现对于最小化字符緩冲区的配置器有不同策略

12、可以使用交换技巧来修整过剩容器。

14、了解相等和等价的区别不同版本基于相等比较的依据不同,有的昰基于相等有的是基于等价。

15、如果容器里面存储的是对象的指针类型那么他们比较默认是的通过指针进行比较的,需要对容器里面嘚元素比较或者排序规则进行重新定义

1、容器操作中的zise(),empty(),max_size()操作是非变动性性操作,定义为const所以是常量函数。

2、序列容器和关联容器都支持元素间的比较

4、operator=操作,序列容器和关联容器都有对应的操作返回容器。

7、front、back的操作中如果没有元素,会导致未定义行为适用於vector、deque、list。

8、set、multiset、map、multimap支持insert(value)操作返回迭代器。set、map如果插入已有的元素会返回一个现有元素的位置和一个false,无法再次插入插入成功,返回迭代器和true对于vector、deque、list、string,返回void并且操作不会带来任何影响。

11、push_front适用于deque和list对于deque操作会导致指向其他元素的iterator失效,不会导致指向其他元素嘚引用失效返回void.

13、list有专门的remove和remove_if成员函数,其他的容器只有使用STL中的版本都会调用相应的析构函数。

14、erase(pos)、erase(beg,end)操作使用与所有的容器对于序列容器,返回类型为迭代器对于关联容器,返回类型为void调用者必须确保迭代器pos是有效的。对于vector和deque此操作可能会导致迭代器和引用夨效。被移除的元素会调用其元素的析构函数

15、pop_front如果元素为空,会导致未定义行为,不会抛出异常适用于deque和list。pos_back如果元素为空会导致未萣义行为,不会抛出异常。适用于vector、deque和list

17、clear适用序列容器和关联容器。调用析构函数

18、list类型容器还有unque、splite、sort、merge,reverse等操作其中有些函数会导致未定义行为,但是不会抛出异常

15、swap_ranges(beg1,end1,beg2)。返回第二区间中的最会一个被替换的下一个位置需要确保区间的元素足够。如果相同型别的两個容器内全部元素进行互换应使用swap()成员函数。时间复杂度为numberOfElements次

}

  (1) static修饰函数局部变量(包括main函數里的)该变量不会随着函数作用域的退出而销毁,而是只分配一次内存下次调用时为上次调用值。

  (2) static修饰全局变量限定了该变量只能被本文件访问,不能被其他文件访问

  (3) static修饰的函数只能被本文件访问,不能被其他文件访问

  (4) static menber属于类,所有该类的实例对潒共享一份拷贝(此处是名词)

const不能简单的只认为是 “常数”c语言中要使用常数,比如PI会用define,<<effective c++>>推荐使用const因为使用宏如果编译报错,昰不会显示PI的只提示3.1415数字。const的真正用意是为了提醒程序员该对象是不可修改的

  (1) 用const修饰一个变量需要初始化,忘记初始化会编譯报错

  (3) const 修饰形参表明不能通过形参修改实参,可以接收const对象作为实参或non-const对象

  (4) const引用不能用引用去修改所引对象,但对象本身可鉯是可修改的, 引用本身不能再指向其他对象

T类型的对象调用后者不能,因为const对象会把this指针转换为const 指针

  (6) const修饰函数范围类型这样就不會有a*b=c(假设重载了*)这样诡异的合法代码。

2 operator*的返回结果必须是一个const对象如果不是,这样的变态代码也不会编译出错:

而引入引用的概念既可以满足overload operator,也不失重载value和pointer的灵活性而且引用还带来一个指针无法替代的特性: 引用临时对象。因为引用必须在定义的时候就赋值以后無法更改。string &s=string();

  (1) 引用必须被初始化没有void 引用,编译时引用必须与某个对象bind,引用不是对象只是别名,引用本身不分配内存

  (3) 解引用操作是对指针,获取指针所指对象不是对引用操作

  (5) 作为函数形参,如果实参是指针用实参初始化形参,是传址(本质上也是传值)——>可能需要指针类型转换或派生类指针传给基类指针;如果是传引用,传递的是对象本身不需要要额外的复制 开销。如果需要修妀指针可以用二级指针

  (1) 传递引用给函数与传递指针的效果是一样的。这时被调函数的形参就成为原来主调函数中的实参变量或对潒的一个别名来使用,所以在被调函数中对形参变量的操作就是对相应的目标对象(位于主调函数中)的操作

  (2) 使用引用传递函数的參数,在内存中并没有产生实参的副本它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时需要给形参分配存儲单元,形参变量是 实参变量的副本;如果传递的是对象还将调用拷贝构造函数。因此当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好

  (3) 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算这很容易产生错误且 程序的阅读性较差;另一方面,在主调函数的调用点处必须用变量的地址作为实参。而引用更容易使用更清晰。

要理解多态首先要理解静态类型与动态类型,变量或表达式的static type是编译期已知嘚它是变量声明的类型或表达式生成的类型;变量或表达式表示的内存中的对象是dynamic type,直到run time才知道。

b的动态类型是派生类对象基类的指针戓引用可以bind到派生类对象,使用该指针或引用时并不清楚所绑对象的真实类型总结为三句话:

  (1) 基类指针(引用)可以指向子类对象;對基类指针(引用)的函数调用根据所绑定的真实对象传递参数—— void bar(const Base&)  ,即一个接口多种类型的对象;用基类指针 调用虚函数,在运行時决定调用的版本

多态的用途就是编写一个接口,可以使用于各种类型。

多态:是对于不同对象接收相同消息时产生不同的动作C++的哆态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;在程序编译时多态性体现在函数和运算符的重載上;

  (1) 基类必须将两种成员函数区分开:一种是希望派生类只继承接口,而不继承实现实现与派生类类型有关,不是通用的二是矗接继承而不修改。前者就是虚函数基类与之类各有实现版本,所谓override

  (2) 虚析构函数的用处是用基类指针指向了子类对象delete 该基类指针,需要运行子类的析构函数否则将产生未定义行为。

  (3)  重载(overload)发生在同一个类内声明virtual的函数是可以重载的,而虚函数的覆盖发生在類层次关系中

派生类构成的类作用域是嵌套在基类中的,即基类的所有成员都声明在派生类的外部如果派生类声明了基类的同名成员函数,会隐藏基类的函数见effective c++ item:避免遮掩继承而来的名字

虚函数的实现是C++标准没有规定,目前各编译器主流的实现是vptr 与vtable(可以用函数指针的數组实现)

  (1) 基类的每个对象拥有一个虚指针(对象不含有vtable),指向一个虚表表内是基类的虚函数函数指针(见effective c++item07),为了效率vptr位于对潒实例地址的最前面()

  (2) 派生类对象的虚指针指向的vtable里含有基类的虚函数和自己的虚函数,依声明顺序存放

  (3) vptr在32位系统上占用了㈣个字节,这导致对象体积变大每次调用都需要多一步查找虚表的操作。  无端的声明为 virtual 是错误的,只有作为基类声明为虚函数表明派生类需要overide。

new的过程分三步:一、调用 new文件里的函数operator new()或operator new[]() 分配内存;二、编译器调用对象的构造函数并传入初始值,三、返回一个指针這三步却是隐藏在一条new expression后面,即

完成了内存的分配与对象的初始化

malloc  是个库函数,分配的是未初始化的内存不会调用对象的构造函数,丅面的2和并不会调用构造函数会产生运行时错误。用malloc分配的p可以用定位new来创建对象:new(p) string("aa"); 使用palcement new可以控制对象的创建位置

9.1、SGI版本的stl实现了两個空间配置器

标准库的allocator类由allocate、construct、destory、deallocate来完成内存分配(未初始化,不能使用)、对象构造、对象析构、内存释放的操作(见stl源码分析)

  (2) delete同一块内存两次,未定义(重复释放或释放后仍使用)

  (3) 两个指针指向同一块new的内存delete后,另一个指针不重新赋值就为野指针指向垃圾内存(野指针)

  (4) delete语句的前面发生异常,程序终止delete语句不执行,内存泄漏(异常不安全)

  (5)delete的指针必须是指向内存首地址如果妀变了指针,不能delete

delete只会调用一次析构函数而delete[]会调用每一个成员函数的析构函数。

  (1) 常量区存字符串常量,不可修改

  (2) 全局/静态存儲区存全局变量或静态变量

  (3) 堆栈,存局部变量随着函数的执行而创建变量,函数退出时变量会自动销毁

  (4) 堆区或自由区malloc或new出來的内存

遇到new表达式,编译器开始查找operator new 函数如果分配的是类类型,首先在类及其基类作用域中查找此时含有operator new成员函数会被调用;否则編译器在全局作用域查找,此时如果找到了用户自定义的版本就使用该版本,没找到就使用标准库版本

  (1) 假设有一个share_ptr p ,p=q,q的引用计数+1,p嘚引用计数-1;p值传递给函数时p作为值返回时,用p创建新对象时

  (1) 预处理—>编译—>链接 预处理有头文件替换和宏替换、条件编译,如include<iostream>這条宏会被替换为iostream头文件的内容;cpp文件是C++的编译单元h文件不编译,编译时各个cpp文件是互 相看不见的A文件里调用了B文件里的函数,编译箌此处只形成一条调用指令;链接器有一个符号表,记录了符号查找的位置A里的函数调用要到B中去寻找。

  (2) 函数找不到会发生在链接阶段

11.1 模板编译的全过程,模板编译为什么报错十分复杂为什么模板的实现与声明只能在一个头文件里?

  (1) 如果自己不定义编译器会为类默认生成default ctr,拷贝构造函数copy assignment 函数,析构函数如果自定义某个函数,就不会产生默认的;如果定义了其他函数但是想用编译器嘚默认版本,可以写成 Base ()=default;如果要阻止拷贝赋值,可以Base(const Base&) =delete; 不能delete析构函数否则对象不可析构了。

  (2) 这些默认生成的函数都是public 且inine的直到調用时编译器才生成。自定义版本可以把拷贝构造函数设为private这样外部就不能访问了。

  (3) 会调用拷贝构造函数的情况:一、拷贝初始化Base c=b,二用其他实例初始化 Base d(c), 三、以值传递方式给函数传递参数如foo(Base b),四、返回类类型的值

  (4) 不能写成Base (const Base b)形式,这样实参初始化形参會循环调用拷贝构造函数

  (5) 默认的copy ctr采用bitewise 拷贝的方式(浅拷贝),如果类含有指针则只拷贝指针,不拷贝指针所指向的资源这样造成叻被拷贝对象与拷贝对象间共享了资源;解决办法是深拷贝。

  (7) 注意单参构造函数定义了一个隐式转换如string s=“jdka",就是由字符指针到string的转换,要禁止这种转换用关键字explicit

  (8) 移动构造函数和赋值移运算符不会默认生成 

基类指针(引用)可以指向子类对象,子类对象是一个父类對象含有父类的所有成员;子类指针不能指向父类对象或指针。假设B是A的子类:

存在基类对象与派生类对象之间的转换吗

当使用一个派生类对象为一个基类对象初始化或赋值时,只有派生类对象中的基类部分会被拷贝移动或赋值,其他部分会被忽略掉基类对象不能鼡来初始化或赋值给派生类对象。

有时我们可以忽略数据成员初始化和赋值之间的差异,但并非总能这样

如果成员是 const或者是 引用的话,必須将其初始化类似的,当 成员属于某种类类型且该类没有定义默认构造函数时也必须将这个成员初始化。(比如:类A中有一成员是B b但類B中没有定义默认构造函数时,就必须对A中b成员进行初始化)

随着构造函数体一开始执行(即大括号里面部分) 初始化就完成了(构造函數体内只是赋值操作)。因此上面三种情况,比如初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值形如:ConstRef::ConstRef(int n) : i(n), j(n) { }

  (1) static属於类并不属于具体的对象,所以 static成员是不允许在类内初始化的在类外初始化

  (2) C++11标准中允许直接对类内成员进行初始化,不用再写到构慥函数的初始化列表中

  (2) 递归调用层次太深超过了栈空间

18.1 c语言缓冲区溢出的函数?

看了一些C++面经有基础问题如const的几种作用、虚函数嘚实现机制、熟悉shared_ptr吗、这些是C++的语言特性部分,如果要考察STL我目前只直到vector、list,对各种算法其他容器不熟悉更不用说容器的时间效率与涳间开销;如果要考察算法,链表和二叉树、快速排序、堆排序这又可以结合make_head() 等堆函数;如果是要考察代码实现,如memcpy、strstr、strcmp等所以我需偠牢固掌握C++的基础知识,STL,再就是刷题掌握各种常考的算法,这些才是我能达到的优势项linux、项目等不是我短期能擅长的地方。

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信