1、C和C++的区别?

  • C是面向过程的结构化编程语言,C++是面向对象的编程语言。

  • C++具有封装、继承和多态三种特性。

  • C和C++动态管理内存的方法不一样,C是使用malloc/free函数,而C++除此之外还有new/delete关键字

  • C中的struct和C++中的类,C中没有类,但是C++中有struct,并且,C++对struct进行了进一步的扩展,使struct在C++中可以和类一样使用。只不过,C++中,struct的成员默认访问修饰符是public,而类中的成员默认访问修饰符时private

  • C++支持函数重载,而C不支持。因为C++的名字修饰与C不同。

  • C++中有引用,C中没有。

  • C中用const修饰的变量不可以用在定义数组时的大小,但是C++中用const修饰的变量可以。

2、static关键字的作用?

  • 全局静态变量

    • 在全局变量前,加上关键字static,全局变量就定义成一个全局静态变量,被存在静态存储区。

    • 初始化:未经初始化的全局静态变量会被自动初始化为0,因为存储在静态存储区

    • 作用域:全局静态变量在声明它的文件之外是不可见的,准确的说是从定义之处开始,到文件结尾。

  • 局部静态变量

    • 在局部变量之前加上关键字static,局部变量就成为一个局部静态变量,被存在静态存储区。

    • 初始化:未经初始化的局部静态变量会被自动初始化为0,因为它同样存储在静态存储区。

    • 作用域:仍为局部作用域,当定义它的函数或者语句块结束时,作用域结束。

  • 静态函数

    • 在函数返回类型前加static,函数就被定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明它的文件中可见,不能为其他文件所用。

    • 注意:不要在头文件中声明static的全局函数,不要再C++内声明非static的全局函数。如果要在多个C++文件中复用某函数,就把该函数的声明放到头文件中去,否则要在C++内部声明需要加上static修饰。

  • 类的静态成员

    • 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则。因此,静态成员是类的所有对象共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储在一处,供所有对象共用。
    • 初始化:类的静态成员必须在类外进行初始化,static修饰的变量先于对象存在,所以static修饰的变量要在类外进行初始化
  • 类的静态函数

    • 静态成员函数和静态数据成员一样,都是属于类的静态成员,都不是对象成员。因此,静态成员函数没有this指针(this指针是指向本对象的指针)。正因为没有this指针,所以静态成员函数不能访问非静态的成员,只能访问static修饰的类成员。

    • static成员函数不能被virtual修饰,static成员不属于任何对象或实例,所以加上virtual没有任何实际意义。虚函数的实现是为每一个对象分配一个vptr指针,vptr指针通过this指针调用,而静态成员函数没有this指针,所以不能被virtual修饰。

3、指针与引用的区别

  • 指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。

  • 引用在被定义的时候必须初始化,并且不能够改变。指针在定义的时候不一定要初始化,并且指向的空间可变。

  • 指针有多级指针,而引用只有一级

  • 指针和引用的自增运算结果不一样。(指针是指向下一个地址空间,引用是引用的变量值加1)。

  • sizeof引用得到的时所指变量(对象)的大小,而sizeof指针得到的是指针本身的大小。

  • 引用访问一个变量是直接访问,而指针访问一个变量是间接访问

  • 使用指针前最好做类型检查防止野指针的出现

  • 引用底层是指针实现的

  • 作为参数时,传指针的实质是传值,传递的值是指针的地址;传引用的实质是传地址,传递的是变量的地址

4、new和malloc的区别

  • new/delete是C++的关键字,需要编译器支持。malloc/free是库函数,需要头文件的支持。

  • new操作符在申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸

  • new操作符在内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,因此new是符合类型安全性的操作符。而malloc操作符在内存分配成功时,返回void指针,需要通过强制类型转换将void *转换成我们需要的类型。

  • new操作符在内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时,会返回null

  • new操作符会先调用operator new函数,申请足够的内存(通常底层使用malloc实现),然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。malloc是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造工作。

5、new和delete的实现原理

  • 对于内置类型

    • new直接调用operator new来分配内存。

    • delete默认调用free释放内存。

    • new[]计算好大小后,调用operator new分配内存。

    • delete[]也只是默认调用free函数释放内存。

  • 对于自定义类型

    • new首先调用operator new分配内存,然后在分配的内存上调用构造函数

    • delete首先调用析构函数,再调用operator delete释放内存

    • new[]先调用operator new[]分配内存,其中会多分配4个字节大小用来保存数组的大小,然后调用n次构造函数。

    • delete[]先执行n次析构函数,然后调用operator delete[]释放空间。

      • 假设指针p指向new[]分配的内存,由于多分配了4个字节,所以实际分配的内存地址为[p-4]。delete[]实际释放的是p-4指向的内存地址**。

6、声明和定义的区别?

  • 变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。

  • 变量声明:用于向程序表明变量的类型和名字。

  • 定义也是声明,当定义变量时,声明了它的类型和名字。

  • extern关键字,通过使用该关键字声明变量名而不是定义它,并且不分配存储空间。

  • 如果声明有初始化式,就被当作定义,即使前面加了extern关键字。

  • 在一个程序中,变量只能定义一次,却可以声明多次。

  • 函数的声明和定义的区别——带{}的是定义,否则就是声明。

7、重载和重写的区别?

  • 重载

    • 表示同一个类中有多个名称相同的方法,但这些方法的参数列表各不相同,即参数个数或类型不同。
  • 重写

    • 是面向对象编程的多态性的一种体现,即C++中的动态多态。在基类中创建虚函数,然后在子类中对父类中的虚函数进行重写,相当于增加一种新的方法。

8、C++中的多态

  • 多态,顾名思义就是同一事物的在不同场景下的多种状态,可以分为静态多态和动态多态。
    • 静态多态包括函数重载和泛型编程。下面代码就是函数重载:
int Add(int left, int right)
{
    return left + right;
}
double Add(double left, int right)
{
    return left + right;
}

int main()
{
    Add(10, 20);
    //Add(10.0, 20.0);  //这是一个问题代码
    Add(10.0,20);  //正常代码
    return 0;
}
  • 可以发现,函数重载是在编译期间完成的,编译器会根据实参类型调用相应的函数,如果有合适的函数就调用,没有就发出警告或错误。
  • 动态多态——重点

    • 在程序运行时,根据基类的引用(指针)指向的对象,来确定自己具体该调用哪个类型的函数。
  • 动态多态的条件

    • 基类中必须包含虚函数,并且要在派生类中对该虚函数进行重写
    • 通过基类对象的引用或指针调用虚函数。
  • 重写

    • 基类中被重写的函数必须是虚函数。
    • 基类和派生类的原型必须保持一致(返回值类型、函数名称及参数列表)。
  • 扩展——以下函数不能作为虚函数:

    • 友元函数

    • 静态成员函数(没有this指针)

    • 构造函数、拷贝构造函数

    • 全局函数

  • 扩展——抽象类

    • 包含纯虚函数的基类,即为抽象类。
  • 纯虚函数

    • 在虚函数的基础上,让虚函数等于0。相当于一个接口,所在类也叫接口类。纯虚函数必须在派生类中实现重写,否则无意义。

9、写一个宏,比较a和b的大小

  • 宏定义发生在预处理阶段,不进行类型检查。
  • 宏没有作用域的限制。
#define MAX (x,y) ((x>y?)x:y)