如何理解封装、继承、多态
封装
可以隐藏实现细节,使得代码模块化;封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
多态
多态指同一个实体同时具有多种形式。在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
C++中,实现多态有以下方法:虚函数,抽象类,覆盖,模板,条件是要有重写,要有继承,父类指向子类。
继承
继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。
虚函数的作用及其实现原理
虚函数实现原理:
** 虚函数表:**
含有虚函数的类包含用于虚函数表。
特别:
- 虚函数表是在编译时创建
-
虚指针是在定义对象时生成,并指向虚函数表
-
虚指针位于对象存储的最前方
构造函数能不能是虚函数
不能
a.构造一个对象时,必须知道对象实际类型,而虚函数是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功,编译器就无法知道对象的实际类型,是该类本身,还是派生类,还是其他。
b.虚函数的执行依赖于虚函数表,而虚函数表是在构造函数中进行初始化的,即初始化虚表指针(vptr),使得正确指向虚函数表。而在构造对象期间,虚函数表(vtable)还没有被初始化,将无法进行。
C++中Overload、Overwrite及Override的区别
Overload(重载):在C++程序中,可以将语义、功能相似的几个函数用同一个名字表示,但参数或返回值不同(包括类型、顺序不同),即函数重载。
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
Override(覆盖):是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
Overwrite(重写):是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
宏函数和inline函数的异同点
- 内联函数在编译时展开,而宏在预编译时展开
- 在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。
- 内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能。
define 和 typedef 区别
(1)原理不同
define是C语言中定义的语法,是预处理指令,在预处理时进行简单而机械的字符串替换,不作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。
typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。
(2)功能不同
typedef用来定义类型的别名,起到类型易于记忆的功能。
define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
(3)作用域不同
define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而typedef有自己的作用域。
C++的内存管理机制
- 栈: 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
- 堆:就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
- 自由存储区:就是那些由 malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
- 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
- 常量存储区: 他们里面存放的是常量,不允许修改。
C语言中的malloc/free和C++中的new/delete的区别和联系
区别1:类型
malloc/free是函数,而new/delete是关键字、操作符
区别2:作用
malloc/free只是简单的进行内存的申请和释放;new/delete除了进行内存申请和释放,还会调用对象的构造函数和析构函数进行空间的初始化和清理
区别3:参数与返回值
malloc/free需要手动计算申请内存的空间大小,而且返回值是void*,需要自己转换成所需要的类型;
new/delete可以自己计算类型的大小,返回为对应的类型指针
c++关键字mutable作用
class Car
{
public:
Car();
~Car();
int getPrice() const; /*调用方法 const成员函数*/
int getLen() const; /*调用方法 const成员函数*/
private:
int m_carPrice;
int m_carLen;
mutable int m_carNum;
};
Car::Car()
{
m_carPrice = 50000;
m_carLen = 5;
m_carNum = 5;
}
Car::~Car()
{
}
int Car::getPrice() const
{
m_carNum++; //此处修改是可以,因为对m_carNum进行了mutable限定
std::cout << m_carPrice << std::endl;
return m_carPrice ; // 无法修改该成员变量
}
int Car::getLen() const
{
return m_carLen; // 无法修改该成员变量
}
在某些场合我们还是需要在const成员函数中修改成员变量的值,被修改的成员变量与类本身并无多大关系,也许你会说,去掉函数的const关键字就行了。 可问题是,我只想修改某个变量的值,其他变量希望仍然被const关键字保护。 这样做相当于在函数中给mutable变量开了特例。
预编译在做些什么事情?
预编译又称为预处理,是做些代码文本的替换工作。主要处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等。就是为编译做预备工作的阶段。
堆和栈的区别,以及为什么栈效率高
堆是由低地址向高地址扩展;栈是由高地址向低地址扩展。
堆中的内存需要手动申请和手动释放;栈中内存是由OS自动申请和自动释放,存放着参数、局部变量等内存。
堆中频繁调用malloc和free,会产生内存碎片,降低程序效率;而栈由于其先进后出的特性,不会产生内存碎片。
堆的分配效率较低,而栈的分配效率较高。
栈的效率高的原因:
栈是操作系统提供的数据结构,计算机底层对栈提供了一系列支持:分配专门的寄存器存储栈的地址,压栈和入栈有专门的指令执行;而堆是由C/C++函数库提供的,机制复杂,需要一些列分配内存、合并内存和释放内存的算法,因此效率较低。