一、简述一下 C++ 的重载和重写?
1. 什么是重载?
重载是指不同的函数使用相同的函数名,但是函数的参数个数或者类型不同(参数列表不同)。调用的时候根据函数的参数来区别不同的函数,函数重载和返回值无关。
2. 如何实现重载?
i) 函数名相同
ii) 必须具有不同的参数列表
iii) 可以有不同的访问修饰符
3. 为什么要重载?
因为重载可以实现函数名相同,但是功能不一样,就是所谓的静态多态。重载是多个函数或者同一个类中方法之间的关系,是平行关系。
4. 什么是重写?
重写也叫覆盖,是指在派生类中重新对基类中的虚函数进行实现。即函数名和参数都一样,只是函数的实现方式不一样,
5. 如何实现重写?
i) 方法声明必须完全与父类中被重写的方法相同
ii) 访问修饰符的权限要大于或者等于被重写的方法的访问修饰符
iii) 子类重写的方法可以加 virtual,也可以不加。
6. 为什么要重写?
重写可以用来实现动态多态,根据调用方法的对象的类型来执行不同的函数。重写是父类和子类之间的关系,是垂直关系。
二、什么是野指针?野指针是如何产生的?如何避免野指针?
1. 什么是野指针?
野指针是指指向的位置是随机的、不可知的、不正确的。
2. 野指针是如何产生的?
2.1 指针变量未初始化或者随便赋值,指针变量没有初始化,其值是随机的,也就是指针变量指向的是不确定的内存,如果对它解除引用,结果是不可知的。
2.2 指针释放后未置空:有时候指针在释放后没有赋值为 nullptr, 虽然指针变量指向的内存被释放掉了,但是指针变量中的值还在,这个时候指针变量就是指向了一个未知的内存,如果对它接触引用,结果是不可知的。
2.3 指针操作超出了变量的作用域:函数中返回了局部变量的地址或者引用,因为局部变量出了作用域就释放了,这时候返回的地址指向的内存也是未知的。
3. 如何避免野指针
3.1 指针变量一定要初始化,可以初始化为 nullptr,因为 nullptr 明确表示空指针,对 nullptr 操作也不会有问题。
3.2 释放后置为 nullptr.
三、虚析构函数有什么作用
1. 虚析构函数的作用?
虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的.
2. 示例
#include<iostream>
using namespace std;
class ClxBase
{
public:
ClxBase() {};
virtual ~ClxBase() { cout<<"delete ClxBase"<<endl; };
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase
{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
int main(int argc, char const* argv[])
{
ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
return 0;
}
输出结果为:
但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:
没有调用子类的析构函数。
所以,C++ 虚析构函数的作用就是:为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
3. 总结
3.1 如果父类的析构函数不加virtual关键字
当父类的析构函数不声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,只调动父类的析构函数,而不调动子类的析构函数
3.2 如果父类的析构函数加virtual关键字
当父类的析构函数声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,先调动子类的析构函数,再调动父类的析构函数。
四、请你说说 C++ 和 C 中struct 的区别以及 C++ 中 struct 和 class 的区别?
1. C 和 C++ 中的 struct 的区别是什么?
在 C 语言中,
1. Struct是用户自定义数据类型(UDT)。
2. c中的struct是没有权限设置的。
3. C中的struct只能是一些变量的集合体,可以封装数据却不可以隐藏数据,而且成员不可以是函数。
4. struct中间的某个类型(例如int)不可以直接初始化。
在 C++ 中,
1. Struct是抽象数据类型(ADT),支持成员函数的定义。
2. C++中struct增加了访问权限,且可以和类一样有成员函数。 C++中的struct等同于class,只是class默认成员权限是private,而struct默认成员权限是public。
3. C++ struct里面成员初始化的形式和类是相同的,不可以直接初始化,就是不可以定义成员的时候同时初始化。
2. C++ 中 struct 和 class 的区别是什么?
C++中的 struct 和 class 就只有一个区别:struct中的成员默认是public的,class中的默认是private的。
此外,如果没有多态和虚拟继承,在C++中,struct和class的存取效率完全相同!简单的说就是,存取class的data member和非virtual function效率和struct完全相同!不管该data member是定义在基类还是派生类的。
如果不是为了和C兼容,C++中就不会有struct关键字。因此建议是:如果不需要与C兼容或传递参数给C程序,不要在C++中用struct。
五、请你简述下 C++的多态?
1. 什么是多态?
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
2. 示例:
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函数
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存储矩形的地址
shape = &rec;
// 调用矩形的求面积函数 area
shape->area();
// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数 area
shape->area();
return 0;
}
上面的示例中,基类 Shape 被派生为两个类。上面的输出结果为:
Parent class area :
Parent class area :
简述 vector 的实现原理
vector 采用的数据结构很简单:线性的连续空间。
vector 是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新的元素。
它以两个迭代器start和finish分别指向配置得来的连续空间中目前已经被使用的空间。迭代器end_of_storage指向整个连续空间的尾部。为了降低空间配置时候的速度,vector实际配置的大小可能比客户端需求量更大一些,以备将来可能的扩充。如果vector在增加一个元素的时候,超过了自身最大的容量。vector则将自身的容量扩充至原来的两倍。扩充空间需要经过的步骤:重新配置空间,元素移动,释放旧内存空间。
一旦vector空间重新配置,则指向原来vector的所有迭代器都失效了,因为vector的地址改变了。