C++面试总结之C++语言特性(一)

1. Volatile

提醒编译器它后面所定义的变量随时都可能改变,因此编译后的程序每次需要存储或读取该变量时都从变量地址中读取数据

Volatile的作用:

(1) 阻止编译器为了提高速度将一个变量缓存到寄存器内不写回

(2) 阻止编译器调整操作volatile变量的指令顺序

但是volatile能够阻止编译器调整顺序,也无法阻止CPU动态调度换序,例如:

C++面试总结之C++语言特性(一)_第1张图片

C++面试总结之C++语言特性(一)_第2张图片

C++面试总结之C++语言特性(一)_第3张图片

 

解决方法:barrier(): 优化内存屏障(联系到内核同步机制)

https://blog.csdn.net/benpaobagzb/article/details/51050633

Barrier I/O硬件级别的同步

应用场景:

(1) 中断服务程序中修改的供其他程序检测的变量

(2) 多任务环境下个任务见共享的标志

(3) 存储器映射的硬件存储器

2.static的用法(区分C语言和C++,两种语言下作用有所不同)。

在C语言中,static的用法:

  (1) 在函数体,一个被声明为静态的变量在函数调用过程中值不变

  (2) 在模块内(函数体外),一个被声明为静态的变量可以被模块内所有函数访问,但不能被模块外其他函数访问,他是一个本地全局变量

  (3) 在模块内,一个被声明为静态的函数只能被该模块内其他函数调用

在C++中,static的用法:

    类的静态成员或方法不属于类实例,而属于类本身,并在所有实例间共享,调用时应用类名+::来引用

3. const关键字的作用

C++面试总结之C++语言特性(一)_第4张图片

      与define相比,const内存效率更高,编译器通常将const 变量保存在符号表中,而不会分配存储空间,这使得它成为一个编译期间的常量,没有存储和读取的操作

4.Explicit的作用

防止执行隐式类型转换

5. 四大转型操作符

static_cast,  const_cast,  dynamic_cast,  reinterpret_cast

语法为:***_cast(expression)

(1) static_cast

基本与拥有与C旧式转型相同的威力与意义,以及相同的限制。如:

int firstNum,secondNum;
double res =(double)firstNum / secondNum;               //旧式C语法
double res =static_cast(firstNum) / secondNum;   //新式C++转型符

(2) const_cast 

用来改变表达式中的常量性(constness)或易变性(volatileness)。如:

int num;
const int *cpNum =#
int *pNum =cpNum;            //error:cannot convert from 'const int *' to 'int *'
int *pNum = (int *)cpNum;                 //旧式C
int *pNum =const_cast(cpNum);     //新式C++const_cast移除常量性

(3) dynamic_cast

用来执行继承体系中“安全的向下转型或跨系转型动作”。如:可以利用dynamic_cast将“指向base class object 的pointer或reference”转型为“指向derived class object的pointer或reference”。如果转型失败,会以一个null指针或一个exception 表现出来(指针转型失败返回null,引用转型失败抛出异常)

class CBase {     };                      
class CDerived:public CBase  {     };     

CDerived dc;
CDerived *dp =&dc;

CBase *bp =dynamic_cast(dp); //使用dynamic_cast将指向继承类的指针转化为指向基类的指针
CBase &br =dynamic_cast(dc);  //使用dynamic_cast将指向继承类的引用转化为指向基类的引用

(4) reinterpret_cast

最常用的用途是转换"函数指针"类型。

typedef void(*funcPtr)();   //funcPtr是个无参数返回值为void型的函数指针类型

int iFunc(){ return0; }     //iFunc为一个无参数返回值为int型的函数
void func( funcPtrf ){} //func函数的参数是一个类型为funcPtr类型的函数指针

int main(){
    func(iFunc());  //error:cannotconvert parameter 1 from 'int' to 'void (__cdecl *)(void)' 
    func(reinterpre_cast(iFunc);  //right!reinterpre_cast将返回值为int 的函数转化为返回值为void的函数
}

补充及修正:

1.static_cast与dynamic_cast

Static_cast 常用于数据类型之间的转换,它也可以实现dynamic_cast中的转换效果,当实现上行转换(继承类转换基类)时,二者可以实现相同的转换效果,当实现下行转换(基类转继承类)时,由于static_cast没有动态类型检查机制,转换可能不安全而正是由于static_cast不作类型检查,它的转换比dynamic_cast要快。

2. reinterpret_cast

reinterpret_cast字面意思是重新解释,主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。

int i;
char *p = "Thisis a example.";
i =reinterpret_cast(p);

此时结果,i与p的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,一个明显的现象是在转换前后没有数位损失。因此,reinterpret_cast允许转换一个指针为其他类型的指针,也允许将一个指针转换为整数类型,反之亦然。这个操作符能够在非相关的类型之间进行转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝,在类型之间指向的内容不做任何类型的检查和转换。这是一个强制转换。使用时有很大的风险,慎用之。比如:

class A { public:int m_a; };
class B { public:int m_b; };
class C : public A,public B {};

C c;
printf("%p, %p,%p\r\n", &c, reinterpret_cast(&c), static_cast(&c));

前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换。因此, 你需要谨慎使用 reinterpret_cast.

6. C++三大特性(继承、多态、封装及具体解释)

(1)封装

封装是在设计类的一个基本原理,是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体。封装是一个抽象的模型,该模型对外提供服务,而任何使用该模型的用户不需要知道模型是如何运作的。

(2)继承

访问权限

public: 所有都能访问

protected: 成员可以被派生类对象访问但不能被该类型的普通用户访问

private:只能由基类的成员和友元访问

基类的protected可以被其派生类访问,派生类protected不能被其基类访问

友元关系不能继承,基类的友元对派生类的成没有特殊访问权限,如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。

(3)多态

多态表示不同对象可以执行相同的操作,但要通过他们自己的实现代码来执行。

7. C++多态、虚函数、重载、重写是什么?虚函数实现原理

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

(1)静态多态:由重载和模板实现。

(2)动态多态:由虚函数实现的。

重载(overload)指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。

重写(也称为覆盖 override)是指派生类重新定义基类的虚函数,virtual。

重定义(也成隐藏):不在同一个作用域(分别位于派生类与基类)

虚函数的实现原理:虚函数表(vtbl),表中每一项指向一个虚函数地址,虚函数表具有继承性和多态性。虚函数表是在编译时就建立了。

8. 构造函数和析构函数是否可以定义为虚函数

构造函数不能,析构函数尽可能定义成虚函数,虚函数表是在构造函数中改写的

析构函数:虚函数

9.内联函数、构造函数、静态成员函数可以是虚函数吗?

都不可以。

内联函数需要在编译阶段展开,而虚函数是运行时动态绑定的,编译时无法展开; 

构造函数在进行调用时还不存在父类和子类的概念,父类只会调用父类的构造函数,子类调用子类的,因此不存在动态绑定的概念;

静态成员函数是以类为单位的函数,与具体对象无关,虚函数是与对象动态绑定的,因此是两个不冲突的概念。

10. 内部类(包含在一个类里面的类)的作用

1)内部类主要是为了避免命名冲突;(内部类定义为public) 

2)为了隐藏名称(内部类定义为private/protected)

11.一个空类多大,一个空类的子类如果也是空,多大,为什么

类的大小:

1.为类的非静态成员数据的类型大小之和.

2.有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).

3.为了优化存取效率,进行的边缘调整.

4 与类中的构造函数,析构函数以及其他的成员函数无关

空类的大小(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以空类的大小为1.

12. 抽象类和虚函数

不能实例化的类被叫做抽象类,它们只提供部分实现,但是另一个类可以继承它并且能创建它们的实例。

"一个包含一个或多个纯虚函数的类叫抽象类,抽象类不能被实例化,进一步一个抽象类只能通过接口和作为其它类的基类使用."

一个抽象类可以包含抽象和非抽象方法,当一个类继承于抽象类,那么这个派生类必须实现所有的的基类抽象方法。

13. 接口、抽象类、集合

接口描述了类的行为和功能,而不需要完成类的特定实现。接口和抽象类都是继承树的上层,他们的共同点如下:
   1) 都是上层的抽象层。
   2) 都不能被实例化
   3) 都能包含抽象的方法,这些抽象的方法用于描述类具备的功能,但是不比提供具体的实现。
他们的区别如下:
   1) 在抽象类中可以写非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这是抽象类的优势;接口中只能有抽象的方法。
   2) 一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类;但是一个类可以实现多个接口。

14.头文件include “”和<>区别

#include <> :引用标准库头文件,编译器从标准库目录开始搜索

#incluce " "  :引用非标准库的头文件,编译器从用户的工作目录开始搜索

15.指针和引用的区别

(1)初始化要求不同:引用在创建时必须初始化,指针不必

(2)可修改性不同:引用一旦被初始化为指向一个对象,他就不能改变为另一个对象的引用,指针则不一样。

(3)不存在空引用,但是存在空指针

(4)测试需要的区别:由于引用不会指向空值,使用引用之前不需要测试其合法性,而指针则需要。因此引用代码效率比指针高。

16.C和c++中struct的区别struct和class区别

C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。

C++中struct能包含成员函数吗? 能!

C++中struct能继承吗?能!!

C++中struct能实现多态吗?能!!!

C语言中struct智能定义成员变量,不能定义成员函数,但是可以有函数指针。

struct和class有什么区别?

1)默认的继承访问权限。struct是public的,class是private的。

2)默认的数据访问控制。struct是public的,class是private的。

3)“class”还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。

17.栈帧是什么?

栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构”。可以简单理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。

(未完待续)

18. 用C实现面向对象(例如多态)

 

19.智能指针

智能指针是一种资源管理类,通过对原始指针进行封装,在资源管理对象进

行析构时对指针指向的内存进行释放;通常使用引用计数方式进行管理,一

个基本实现如下:

20. 函数模板 类模板

https://blog.csdn.net/xiaoding133/article/details/11662183 

模板特例化用于当一个数据类型需要进行不同的处理和实现的情况。

#include 
using namespace std;

template 

T multIt(T x) {
    for(int ii=0; ii(xx) << endl;;
}

类模板:

类模板定义:template  class MyTemplateClass { ... };

类模板特例化:template <> class MyTemplateClass  { ... };

      函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。即函数模板允许隐式调用和显式调用而类模板只能显示调用。这期间有涉及到函数模板与模板函数,类模板与模板类的概念 (类似于类与类对象的区别)

     使用模板的缺点:不当的使用模板会导致代码膨胀,集二进制代码臃肿松散,影响程序运行效率

     解决方法:把C++模板中与参数无关的代码分离出来

21. 模板特例化

(1)函数模板特例化

(2)类模板特例化

(3)部分模板特例化 全部模板特例化

(4)使用函数模板对普通函数进行泛型化

你可能感兴趣的:(C++面试总结)