1 C/C++概念

1.1 简述C++的特点

  • 支持面向对象程序设计,包括封装、继承和多态三大特征
  • 更加安全,增加了const变量、引用&、智能指针、try_catch等
  • 引入了模板的概念,可复用性高
  • 跨平台

1.2 各种变量之间的区别

主要看生命周期作用域

1.3 new和malloc的区别

new是一个操作符,malloc是一个函数

malloc内存分配成功返回的是void*,需要强制类型转换,而new无需进行

malloc分配内存失败会返回NULL,new则返回bac_alloc的异常

1.4 有了malloc/free 为什么还要new/delete ?

malloc/free是函数,无法在创建对象时自动执行构造函数,也无法再消亡时自动执行析构函数,因此需要new和delete

new创建的是一个对象,malloc分配的是一块内存区域

1.5 指针函数和函数指针的区别?

函数指针:指向函数的指针变量,可以通过指针调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int add(int a,int b){
return a+b;
}
int sub(int a,int b){
return a-b;
}
//函数指针
int (*func)(int a,int b);

int main()
{
func = &add;
printf("add:%d\n",(*func)(10,5));
func = &sub;
printf("sub:%d\n",(*func)(10,5));
return 0;
}

指针函数:返回值为指针的函数,比如,int * func()

1.6 野指针

指向“垃圾”内存的指针,由于未初始化指针数据已删除超出作用域都会产生

2 面向对象

2.1 什么是面向对象

一种软件开发方法,也是一种编程范式,将相关数据和对这些数据的操作组织称一个整体,成为“对象”。

2.2 面向对象的三大特征

封装、继承、多态

  • 封装:将属性和方法打包在一起,形成一个“类”,并对其加以权限控制
  • 继承:让某个类型的对象获得另外一个类型对象的属性和方法的机制。
  • 多态:通过基类指针可以调用任何派生类的函数。多态分为编译时的多态(重载),运行时的多态(虚函数)。

三大特性使得编程更加灵活,提高代码的可读性、可复用性、可维护性

2.3 RAII

Resource Acquisition Is Initialization 资源获取初始化,是C++的一种资源管理策略,将资源的生命周期和对象的生命周期绑定在一起,资源的获取和释放是通过构造和析构函数

2.4 组合和继承

继承:一个类 继承 另一个类 的属性和操作

组合:一个类 包含 另一个类的对象做成员

1
2
3
4
5
6
7
class Engine{};
class Tire{};
class Car {
private:
Engine engine;
Tire tires[4];
}

继承的缺点:子类和父类的耦合度高,多层继承导致复杂的关系,代码可维护性变差

2.5 为什么基类的析构最好为虚析构?

防止内存泄漏,如果想通过基类释放派生类的资源,没有虚函数就只会调用基类的析构函数,派生类的资源就没办法释放

2.6 重写和重载

重写(override)指的是派生类重新实现基类的虚函数

重载(Overload)是指在同一个作用域内允许多个函数具有相同的名称,但是参数列表不同(包括参数数量、类型或顺序)

2.7 delete 和 default

对于默认构造函数,只有在没有声明其他任何构造函数的情况下,才会自动的生成,否则就需要开发者去手工定义无参的构造函数,下面这段代码就会报错

1
2
3
4
5
6
7
8
9
10
11
class classB { 
public:
classB(int input) {
m_var1 = input;
}
private:
int m_var1 = 0;
};
int main() {
classB a = classB();//会提示无法找到函数classB::classB()
}

但是默认的构造函数效率比较高,我们如果想用默认的无参构造就可以用default关键字

1
2
3
4
5
6
7
8
9
10
11
12
class classB { 
public:
classB() = default; //使用默认无参构造
classB(int input) {
m_var1 = input;
}
private:
int m_var1 = 0;
};
int main() {
classB a = classB();
}

前面智能指针样例里说到,我们可能会希望定义的类,不允许通过拷贝或者赋值的方式初始化,只允许默认的构造函数,那么就可以使用delete关键字

1
2
3
4
5
6
7
8
9
10
11
class classB {
public :
classB() = default;
classB(const classB& b) = delete;
private: int m_var1 = 0;
};
int main()
{
classB b = classB();
classB a(b);//这里会编译错误 无法引用 函数 "classB::classB(const classB &b)"
}

3 其他

3.1 extern “C”

告诉编译器,应该按照C语言的方式进行处理

在C++的函数中,编译器会对函数名包含额外的信息(参数类型等),C语言就不会,因此C++函数和C语言函数链接的时候就不兼容。

extern “C”只会影响声明函数,不会影响内部函数的C++代码实现

3.2 模板

模板是泛型编程,手写的一个模板函数可以在编译阶段解释出很多个不同类型的函数

3.3 map和unordered_map有什么区别

map基于红黑树,能确保元素的有序

unordered_map基于哈希表,不保证元素的有序性

3.4 mutable

mutable关键字可以应用于类定义的成员变量,表示这个成员变量可以在常量函数中被修改

1
2
3
4
5
6
7
calss myclss{
public:
void f1() const {count++};
void f2(){count++};
private:
mutable int count;
}

上面的操作都是可以的