针对算法方向或者应届毕业生对c++有需求,但是要求不高的,如果资深c++算是十级的话,这篇文章的难度,仅仅将尽2-3级。
c++:
1:new、delete、malloc、free关系?
a.属性
new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持c。
b.参数
使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
c.返回类型
new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
e. 分配失败
new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
f.自定义类型
new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
2:多态
为什么有多态?多态的意义?:这个就不用说了。
多态通过什么实现?:虚函数。虚函数是指一个类中你希望重载的成员函数 ,当你用一个 基类指针或引用 指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本。
虚函数在实际项目中如何应用?:父类指针指向子类的对象,当调用父类中的虚函数的时候,就会执行子类的对应方法。具体例子请看这里:https://www.cnblogs.com/weiyouqing/p/7544988.html
虚函数的原理?:要点是要答出虚函数表和虚函数表指针的作用
虚函数是用来实现动态绑定的。
C++中虚函数使用虚函数表和虚函数表指针实现,虚函数表是一个类的虚函数的地址表,用于索引类本身以及父类的虚函数的地址,假如子类重写了父类的虚函数,则对应在虚函数表中会把对应的虚函数替换为子类的函数的地址(子类中可以不是虚函数,但是必须同名);虚函数表指针存在于每个对象中(通常出于效率考虑,会放在对象的开始地址处),它指向对象所在类的虚函数表的地址;在多继承环境下,会存在多个虚函数表指针,分别指向对应不同基类的虚函数表。
虚函数表是每个(有虚函数的)类对应一个。虚函数表指针是每个对象对应一个。
虚函数表里只能存放虚函数,不能存放普通函数。
如果一个函数不是虚函数,那么对它的调用(即该函数的地址)在编译阶段就会确定。调用虚函数的话(它的地址)要运行时才能确定。
虚函数的函数入口是动态绑定的。在运行时,程序根据基类指针指向的实际对象,来调用该对象对应版本的函数。(用该对象的虚函数表指针找到其虚函数表,进而调用不同的函数。)(只有是虚函数的情况下才会这么做(用虚函数表指针去查虚函数表)。非虚函数直接就调用自己的。
为什么需要虚析构函数?(什么情况下要用虚析构函数?):
在存在类继承并且析构函数中需要析构某些资源时,析构函数需要是虚函数。否则若使用父类指针指向子类对象,在delete时只会调用父类的析构函数,而不能调用子类的析构函数,造成内存泄露。
注:纯虚函数写法上就是比虚函数多写 =0. 纯虚函数是子类必须实现的。
以上就是虚函数部分,这个深度应该对应届毕业生 应该足够了,如果再深就有些变态了。
3 :种强制类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast
static_cast: 1)完成基础数据类型,2)同一个继承体系中类型的转换 3)任意类型与空指针类型void*之间的转换。
dynamic_cast:使用多态的场景,增加了一层对真实调用对象类型的检查。
const_cast去除指针或引用的const属性。
1、转化常量指针为非常量的指针,并且仍然指向原来的对象;
2、转化常量引用为非常量的引用,并且仍然指向原来的对象;
3、const_cast一般用于修改指针。如const int *ptr形式。
reinterpret_cast:进行无关类型的转换(不必做过多了解)
使用场景:https://www.cnblogs.com/zeppelin5/p/10075569.html
4:C++内存对齐
1、第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、在数据成员完成各自对齐之后,类(结构或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
不懂得 自行百度,这个问题 很少面试会问,都是做笔试题,可能会有。
5:如何解决类之间的相互依赖问题?
在实际写代码时经常会碰到 class A 中用到class B 而class B中也用到了class A,这种情况下就会报错,某个方法不是类中的member。
这种情况下, 此种状况的解决利用前置声明定义的那个类中的保持另外一个类的引用定义为指针,定义指针时不需要对那个类的定义可见。注意 头文件中利用前置声明,这样的话一定不能写定义,定义一定要在cpp中实现,这个cpp中#include class A的头文件。
https://www.csdn.net/gather_22/Ntjagg4sMTYtYmxvZwO0O0OO0O0O.html
6:如何解决头文件的循环引用问题?
为了防止两次include同一个头文件 ,这个问题涉及到一个写代码的习惯问题,写头文件时一定要加#ifndef这一套。
7:智能指针的种类和原理:
8:多线程之间的通信:
9:你所了解的c++常见的标准库
10:设计模式
如何写一个线程安全的单例模式?
单例模式不用过多赘述,就是一个项目中,只存在一个这个类的对象,实现的原理就是将这个类的构造函数设为private,对外只提供一个static的GetInstance的接口获取这个类的静态对象,当然这个静态对象也是private的,需要注意的是,在实现文件中,一定要将这个静态unique_instance初始化为NULL,因为静态变量 一定要初始化。下面说一下线程安全的单例模式
懒汉模式:
template classsingleton{
protected:
singleton(){};
private:
singleton(constsingleton&){};
singleton&operator=(constsingleton&){};
staticT* m_instance;public:
staticT* GetInstance();
};
template T* singleton::GetInstance()
{
if( m_instance == NULL)
{
m_instance =new T();
}
return m_instance;
}
template T* singleton::m_instance = NULL;
在定义m_instance变量时先等于NULL,在调用GetInstance()方法时,在判断是否要赋值。这种模式,并非是线程安全的,因为多个线程同时调用GetInstance()方法,就可能导致有产生多个实例。要实现线程安全,就必须加锁。
template classsingleton{
protected:
singleton(){};
private:
singleton(constsingleton&){};
singleton&operator=(constsingleton&){};
staticT* m_instance;
static pthread_mutex_t mutex;public:
staticT* GetInstance();
};
template <class T>T* singleton<T>::GetInstance()
{
if( m_instance == NULL)
{
pthread_mutex_lock(&mutex);
if( m_instance == NULL)
{
T* ptmp =new T();
m_instance = ptmp;
}
pthread_mutex_unlock(&mutex);
}
return m_instance;
}
template pthread_mutex_t singleton::mutex = PTHREAD_MUTEX_INITIALIZER;
template T* singleton::m_instance = NULL;