面试题总结
1.int f(int a)能重载void f(int a)吗?重载是在编译期还是运行期?
答:不能重载,因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。 重载是在编译器,虚函数多态(重写)才是在运行期。
2.拷贝构造函数作用及用途?什么时候需要自定义拷贝构造函数?
答:(1)在C++中,有下面三种对象需要拷贝的情况:
a.一个对象以值传递的方式传入函数体
b.一个对象以值传递的方式从函数返回
c.一个对象需要通过另一个对象进行初始化
以上的情况就需要拷贝构造函数的调用
(2)当类中数据成员需要动态分配存储空间时,不可以依赖default copy constructor 。当default copy constructor被编译器需要而和成时,将执行default memberwise copy 语义。
此时如果类中有动态分配的存储空间时,将会发生惨重的灾情。
3.构造函数可以调用虚函数吗?
答:不能,语法上通过,语义上有问题。
derived class对象内的base class成分会在derived class自身构造之前构造完毕。因此,在base class的构造函数中执行的virtual函数将会是base class的版本,决不会是derived class的版本。
即使目前确实正在构造derived class。
4.c++11 有哪些改进?有哪些东西 ?
答: lambda 线程库 智能指针 auto
5.如果实现一个不能在对上分配的类?如果实现一个不能被继承的类?
答:在堆上分配就需要用到new操作符,重载new操作符并将其放于private内。 这个用到C++ 11新关键字final
class B final { public: B(int a) { } };
现在就不能继承了
6.引用和指针右什么区别?
答:
(1)定义一个指针变量编译器会为它分配内存,而引用不占用任何内存;
(2)引用必须在定义时被初始化,指针不必;
(3)不存在指向空值的引用,但存在指向空值的指针。
7.函数参数传递中值传递、地址传递、引用传递有什么区别?
答:
(1) 值传递,会为形参重新分配内存空间,将实参的值拷贝给形参,形参的值不会影响实参的值,函数调用结束后形参被释放;
(2) 引用传递,不会为形参重新分配内存空间,形参只是实参的别名,形参的改变会影响实参的值,函数调用结束后形参不会被释放;
(3) 地址传递,形参为指针变量,将实参的地址传递给函数,可以在函数中改变实参的值,调用时为形参指针变量分配内存,结束时释放指针变量。
8.static关键字有什么作用?
答:
(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时维持上次的值;
(2)在模块内的static全局变量可以被模块内所用函数调用,但不能被模块外其他函数访问;
(3)在模块内的static函数只能被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的static成员变量属于整个类拥有,对类的对象只有一份拷贝;
(5)在类中的static成员函数属于整个类拥有,这个函数不接收this指针,因而只能访问static成员变量。
9.const关键字有什么作用?
答:
(1)阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它初始化,因为以后就没机会改变它了;
(2)对指针来说,可以指定指针本身为const,也可指定指针所指的数据为const,或二者同时指定为const;
(3) 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4) 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5) 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为左值。
10 .链表和数组的区别在哪里?
(1) 链表和数组都可以叫线性表,数组又叫顺序表,主要区别在于,顺序表是在内存中开辟一段连续的空间来存储数据,而链表是靠指针来连接多块不连续的空间,在逻辑上形成一片连续的空间来存储数据;
(2) 数组要求空间连续,占用总空间小,链表不要求空间连续,占用总空间大;
(3) 数组方便排序和查找,但删除和插入较慢;链表方便删除和插入,但查找较慢,不方便排序。
11.进程和线程的差别?
答:
线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程也可以并发执行;
(3)拥有资源:进程是拥有资源的一个独立单元,线程不拥有系统资源但可以访问隶属于进程的资源;
(4)系统开销:在创建或撤销进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤销线程时的开销。
12.C++重写、重载、重定义的区别?
(1)成员函数重载特征:
a.相同的范围,在同一个类
b.函数名字相同
c.参数不同
(2)重写(覆盖)是指派生类函数覆盖基类函数,特征是:
a.不同的范围,分别位于基类和派生类中
b.函数的名字相同
c.参数相同
d.基类函数必须有virtual关键字
(3)重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
a.如果派生类的函数和基类的函数同名,但是参数不同,此时不管有无virtual,基类的函数被隐藏;
b.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类函数被隐藏。
13.一个数据成员是否可以既是const又是static,如果不行,为什么?
(1)一个数据成员可以既是const又是static,表示为静态常量;
(2)常量一般在构造函数后初始化;
(3)静态成员一般在类外初始化;
(4)静态常量在类外初始化,但要在类外初始化的同时声明为const。
14.构造函数与析构函数的异同点?
答:
1.构造函数有如下特点:
(1)构造函数的名字必须与类名相同;
(2)构造函数可以有任意类型的参数,但不能有返回类型;
(3)定义对象时,编译系统会自动调用构造函数;
(4)构造函数是特殊的成员函数,函数体可以在类体内也可以在类体外;
(5)构造函数被声明为公有函数,但它不能像其他成员函数那样被显式调用,它是在定义对象的同时被调用的。
2.析构函数有如下特点:
(1)析构函数的名字必须与类名相同,但它前面必须加一个波浪号;
(2)析构函数没有参数,也没有返回值,而且不能被重载,因此在一个类中只能有一个析构函数;
(3)当撤销对象时,编译系统会自动调用析构函数;
(4)析构函数可以是virtual,而构造函数不能是虚函数。
15.简述C++异常处理方式?
答:
一个典型的C++异常处理包含以下几个步骤:
(1)程序执行时发生错误;
(2)以一个异常对象(最简单是一个整数)记录错误的原因及相关信息;
(3)程序监测到这个错误(读取异常对象);
(4)程序决定如何处理错误;
(5)进行错误处理,并在此后恢复/终止程序的执行。
16.成员函数和友元函数的区别?
(1)成员函数是类定义的一部分,通过特定的对象来调用。成员函数既可以隐式访问调用对象的成员,而无须使用成员操作符;
(2)友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式访问类成员,而必须将成员操作符用于作为参数传递的对象。
17.函数模板与函数重载的异同?
(1)函数的重载是指定义了几个名字相同,但参数的类型或参数的个数不同的函数;
(2)模板函数是指的几个函数的具体算法相同,而参数类型不同的函数;
(3)模板函数可以减少重载函数,但也可能引发错误。
18.面向对象的三大特征是什么?
面向对象的三个基本特征:封装、继承、多态。
19.什么是封装?
(1)封装是面向对象的特征之一,是对象和类概念的主要特性;
(2)封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏;
(3)在C++中类中成员的属性有:public、protected、private,这三个属性的访问权限依次降低。
20.什么是继承?
(1)继承是指:可以使用现有类的所有功能,并在无须重新编写原来的类的情况下对这些功能进行扩展;
(2)通过继承创建的类称为“子类”或“派生类”;
(3)被继承的类称为“基类”、“父类”或“超类”;
(4)在某些OOP语言中,一个子类可以继承多个基类,但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现;
(5)C++中可以用public、protected、private来修饰继承特性。
21.什么是多态?
(1)多态性:允许将父类对象设置为和一个或更多的它的子对象相等的技术,赋值之后,父对象可以根据当前赋值给它的子对象的特性以不同的方式运作。简单地说,允许将子类类型的指针赋值给父类型的指针;
(2)实现多态的两种方式:覆盖、重载;
(3)覆盖:子类重新定义父类的虚函数;
(4)重载:允许存在多个同名函数,而这些函数的参数表不同。
22.不可以同时用const和static修饰成员函数。
C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的。我们也可以这样理解:两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。
23.类型安全以及C++中的类型转换?
四种类型转换:
- static_cast <T*> (content) 静态转换.在编译期间处理,可以实现C++中内置基本数据类型之间的相互转换。如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数。
- dynamic_cast<T*>(content) 动态类型转换;也是向下安全转型;是在运行的时候执行;基类中一定要有虚函数,否则编译不通过。在类层次间进行上行转换时(如派生类指针转为基类指针),dynamic_cast和static_cast的效果是一样的。在进行下行转换时(如基类指针转为派生类指针),dynamic_cast具有类型检查的功能,比static_cast更安全。
- const_cast<T*>(content) 去常转换;编译时执行;
- reinterpret_cast<T*>(content) 重解释类型转换;
24 C语言和C++有什么区别?
C语言是结构化的编程语言,它是面向过程的,而C++是面向对象的。
封装:将数据和函数等集合在一个单元中(即类)。被封装的类通常称为抽象数据类型。封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
继承:继承主要实现重用代码,节省开发时间。它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向派生类的基类指针,来调用实现派生类中的方法。有编译时多态和运行时多态。
25.如何实现类对象只能静态分配或动态分配?
只能动态分配:
class A { protected: A(){} ~A(){} public: static A* create(){return new A();} void destory(){delete this;} };
只有使用new运算符,对象才会被建立在堆上。因此只要限制new运算符就可以实现类对象只能建立在栈上。可以将new运算符设为私有。
class A { private: void* operator new(size_t t){} //注意函数的第一个参数和返回值都是固定的 void operator delete(void* ptr)() //重载了new就需要重载delete public: A(){} ~A(){} };
26auto_ptr类与shared_ptr类?
从c++11开始, auto_ptr已经被标记为弃用, 常见的替代品为shared_ptr。shared_ptr的不同之处在于引用计数, 在复制(或赋值)时不会像auto_ptr那样直接转移所有权。 两者都是模板类,却可以像指针一样去使用。只是在指针上面的一层封装。
auto_ptr实际也是一种类, 拥有自己的析构函数, 生命周期结束时能自动释放资源,正因为能自动释放资源, 特别适合在单个函数内代替new/delete的调用, 不用自己调用delete,也不用担心意外退出造成内存的泄漏。
atuo_ptr的缺陷:
- auto_ptr不能共享所有权,即不要让两个auto_ptr指向同一个对象(因为它采用的是转移语义的拷贝,原指针会变为NULL)。
- auto_ptr不能管理对象数组(因为它内部的析构函数调用的是delete而不是delete[])。
- auto_ptr不能作为容器对象,STL容器中的元素经常要支持拷贝,赋值等操作,在这过程中auto_ptr会传递所有权。
-
shared_ptr 使用引用计数的方式来实现对指针资源的管理。同一个指针资源,可以被多个 shared_ptr 对象所拥有,直到最后一个 shared_ptr 对象析构时才释放所管理的对象资源。
可以说,shared_ptr 是最智能的智能指针,因为其特点最接近原始的指针。不仅能够自由的赋值和拷贝,而且可以安全的用在标准容器中。
27. C++是不是类型安全的?
答:不是,两个不同类型的指针之间可以可以强制转换(reinterpret cast)