面试我爱编程C++面试

技术面试那些事儿:C++篇

2017-08-24  本文已影响78人  geekzph

struct与class的区别

定义一个空的类型,无任何成员变量和成员函数。在该类型中添加一个构造函数和析构函数。把析构函数标记为虚函数呢?一个只含有虚函数的类的size为多少

含虚函数类的虚函数表是存放在哪里?

C++虚函数的具体实现原理

基类定义了虚函数,子类可以重写该函数,当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态地调用属于子类的该函数,且这样的函数调用是无法在编译期间确认的,而是在运行期确认,也叫做迟绑定。在此模型中,non static 数据成员被放置到对象内部,static数据成员, static and nonstatic 函数成员均被放到对象之外。对于虚函数的支持则分两步完成:
1.每一个class产生一堆指向虚函数的指针,放在表格之中。这个表格称之为虚函数表(virtual table,vtbl)。
2.每一个对象被添加了一个指针,指向相关的虚函数表vtbl。通常这个指针被称为vptr。vptr的设定和重置都由每一个class的构造函数,析构函数和拷贝赋值运算符自动完成。

介绍C++内存管理(C ++的内存模型是热门问题)

在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

C/C++的区别?

面向对象的特点,简单描述一下?

一、三个基本特征
向对象的三个基本特征是:封装、继承、多态。

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
Ø 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态,有二种方式,覆盖,重载。
覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)

inline函数的作用

C++内联函数提供了替代函数调用的方案,通过inline声明,编译器首先在函数调用处使用函数体本身语句替换了函数调用语句,然后编译替换后的代码。因此,通过内联函数,编译器不需要跳转到内存其他地址去执行函数调用,也不需要保留函数调用时的现场数据。

虚函数的作用

虚函数的作用就是实现“动态联编”,也就是在程序的运行阶段动态地选择合适的成员函数。具体的实现方式是:在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。编译器在编译过程中发现类的函数名前的关键字virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数,

纯虚函数的作用,纯虚函数能否被实例化

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”virtual void funtion1()=0

引入原因:
1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

含有纯虚函数的类为抽象类,不能声明对象,只是作为基类为派生类服务。除非在派生类中完全实现基类的所有虚函数,否则派生类也是抽象类,不能实例化对象。
抽象类不能定义对象,但是可以作为指针或或者引用类型使用。

类中哪些函数不能声明为虚函数,原因

常见的不不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。

析构函数能否为虚函数,为什么?

有多态的基类应该声明一个virtual析构函数。如果class带有任何virtual函数,他就应该拥有一个virtual析构函数。若基类的析构函数不是虚函数,当基类指针pa指向用new运算符生成的派生类对象B时,delete基类指针,只会运行基类的析构函数,而不会执行派生类的析构函数。

你对c++内存这里有什么见解,或者有什么好的设计理念

自己总结了几点,new,detele,new[],delete[],构造析购里面通常怎么设计,虚析构函数的概念 ,后来他又给我讲解了一下c++喜欢用namespace这种机制,类似于析购构造的原理,一段结束后自动释放的原理。
我接着也说了一下自己对namespace经常用到的地方(1)自动锁的原理经常用到namespace,起到自动调用析购的函数(2)还有喜欢把一个class也封装在一个namespace里面,

实现编译器处理虚函数表应该如何处理

C++内存分配

堆、栈、自由存储区、全局/静态存储区和常量存储区

程序编译链接的过程和函数找不到在哪个阶段报错

动态绑定的介绍

动态绑定:运行时绑定,通过地址实现
只有采用“指针->函数()”或“引用变量.函数()”的方式调用C++类中的虚函数才会执行动态绑定。对于C++中的非虚函数,因为其不具备动态绑定的特征,所以不管采用什么样的方式调用,都不会执行动态绑定。
即所谓动态绑定,就是基类的指针或引用有可能指向不同的派生类对象,对于非虚函数,执行时实际调用该函数的对象类型即为该指针或引用的静态类型(基类类型);而对于虚函数,执行时实际调用该函数的对象类型为该指针或引用所指对象的实际类型。

引用是否能实现动态绑定,为什么引用可以实现

可以,引用和指针可以实现动态绑定

介绍所有的构造函数

默认构造函数
构造函数
复制构造函数

什么情况下要给类写拷贝构造函数

深拷贝时。类具有指针成员时。

成员初始化列表的概念

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
2.const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

为什么用成员初始化列表会快一些

初始化列表是直接初始化,利用的函数匹配原理,调用的是构造函数或者copy构造函数(根据初始化的参数来决定,内置类型为构造函数,类类型为copy构造函数)。在函数体里复制,会多调用一次默认构造函数。

C语言变量存放位置

C++垃圾回收,shared_ptr的引用计数出现循环引用怎么办

C中的malloc和free做了哪些事情,free怎么知道free多长,C++中的delete又怎么知道delete多长

在heap中进行内存分配,以链表形式进行记录。free时先判断是不是malloc分配的,然后释放。将free标志设置为1

指针和引用的区别?

  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。

6野指针的问题。

1、指针变量没有被初始化。 任何指针变量在刚被创建的时候不会自动成为NULL指针,它的缺省值是随机的。所以指针变量在创建的时候,要么设置为NULL,要么指向合法的内存。
2、指针p被free/delete之后,没有置为NULL(最好加一句p = NULL;)。他们只是把指针指向的内存给释放掉,并没有把指针本身干掉。
3、指针操作超越了变量的作用范围。不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

指针(各种指针,指针数组,数组指针,函数指针,n级指针)

char * a=new char a[]; sizeof(a)大小

4字节,指针的大小。

静态动态数组区别

动态数组可以再编译时确定数组大小。存储的位置不一样。

new delete new[] delete [] malloc free底层实现

http://blog.codinglabs.org/articles/a-malloc-tutorial.html

overlode override
77.unsigned long* p1=(unsigned long* )0x810000(地址)
unsigned char* p2=(unsigned char* )0x810000(地址)
p1+5=? p2+5=?

能否用memset实例化一个类?

不能,禁止用memcpy、memset初始化非POD对象。由于非POD类型比如非集合类型的class对象,可能存在虚函数,内存布局不确定,跟编译器有关,滥用内存拷贝可能会导致严重的问题。
无法复制虚函数表。

new/delete和malloc/free的区别

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。

* malloc/free的使用要点

void * malloc(size_t size);
int *p = (int *) malloc(sizeof(int) * length);

我们应当把注意力集中在两个要素上:“类型转换”和“sizeof”。

* new/delete的使用要点

int *p1 = (int *)malloc(sizeof(int) * length);
int *p2 = new int[length];

这是因为new内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new在创建动态对象的同时完成了初始化工作。

链表和数组的区别

const 和 static 用法。

这个比较多, 详细的可以自己在网上找找。
• const 关键字: 用在局部变量上, 用在类成员变量上, 用在类成员函数后面...
• static 关键字: 用在全局变量上, 用在局部变量上,用在类成员变量上, 用在类成员函数上...

计算下面几个类的大小

•   class A {};: sizeof(A) = 1; 
•   class A { virtual Fun(){} };: sizeof(A) = 4(32位机器)/8(64位机器); 
•   class A { static int a; };: sizeof(A) = 1; 
•   class A { int a; };: sizeof(A) = 4; 
•   class A { static int a; int b; };: sizeof(A) = 4; 

已知某类:

class A
{
public:
A(int x){}
}
问:A a = 1;是否正确, 如果正确, 那么它调用了哪些函数?
• 由于 A 类的构造函数没有声明为explicit因此可以 int 类型强制转换来。因此, 该表达式正确。
• 对于没有优化过的编译器: 先将 1 转化为 A 类型(构造函数), 再将其赋值给 a 变量(拷贝赋值函数)。
• 对于优化了的编译器: 直接用 1 来构造变量 a(构造函数)。

代码找错:

char* str = "hello"; 1)
char* p = new char[5]; 2)
strcpy(p, str); 3)
cout << p << endl; 4)
• 编译通过, 运行时出错。
• 2) 中的数组申请太小, 无法容纳下 "hello" 的大小。 因为字符串后面还有结束符('\0')。
• 与 new 搭配的是 delete。2) 中 new 了资源没有 delete 掉。

class {}内部实现的函数。

默认构造函数, 默认析构函数, 拷贝赋值函数, (移动构造函数, 移动赋值函数) 析构函数,赋值运算符

C 语言的特性

C 是面向对象的语言, 自然有面向对象的语言的特性: 封装, 继承, 多态。
C 也是泛型编程语言, 自然也有泛型编程语言的特性。

重载和重写的区别

重载要求相同的函数名, 不同的参数列表。仅返回值不同的函数不能重载。
重写要求父类是虚函数, 子类改写其函数体。要求函数签名一模一样。

对象复用的了解
零拷贝的了解

手写实现智能指针类
1,方法重载,何时用重载,为什么要使用重载?而不是把一个方法名字换成不同的。
菱形继承与接口目的
• 面向对象的三大特性
• MVC设计模式

上一篇 下一篇

猜你喜欢

热点阅读