面向对象基础
能够准确理解下面这些问题是从C程序员向C++程序员进阶的基础。当然了,这只是一部分。
-
面向对象三大特性?
-
封装性:数据和代码捆绑在一起,避免外界干扰和不确定性访问。
-
继承性:让某种类型对象获得另一个类型对象的属性和方法。
-
多态性:同一事物表现出不同事物的能力,即向不同对象发送同一消息,不同的对象在接收时会产生不同的行为(重载实现编译时多态,虚函数实现运行时多态)。
-
-
public/protected/private的区别?
-
public的变量和函数在类的内部外部都可以访问。
-
protected的变量和函数只能在类的内部和其派生类中访问。
-
private修饰的元素只能在类内访问。
-
-
对象存储空间?
-
非静态成员的数据类型大小之和。
-
编译器加入的额外成员变量(如指向虚函数表的指针)。
-
为了边缘对齐优化加入的padding。(内存对齐)
-
-
C++空类有哪些成员函数?
-
首先,空类大小为1字节。
-
默认函数有:
-
构造函数
-
析构函数
-
拷贝构造函数
-
赋值运算符
-
-
-
构造函数能否为虚函数,析构函数呢?
-
析构函数:
-
析构函数可以为虚函数,并且一般情况下基类析构函数要定义为虚函数。
-
只有在基类析构函数定义为虚函数时,调用操作符delete销毁指向对象的基类指针时,才能准确调用派生类的析构函数(从该级向上按序调用虚函数),才能准确销毁数据。
-
析构函数可以是纯虚函数,含有纯虚函数的类是抽象类,此时不能被实例化。但派生类中可以根据自身需求重新改写基类中的纯虚函数。
-
-
构造函数:
- 构造函数不能定义为虚函数。在构造函数中可以调用虚函数,不过此时调用的是正在构造的类中的虚函数,而不是子类的虚函数,因为此时子类尚未构造好。
-
-
构造函数调用顺序,析构函数呢?
-
调用所有虚基类的构造函数,顺序为从左到右,从最深到最浅
-
基类的构造函数:如果有多个基类,先调用纵向上最上层基类构造函数,如果横向继承了多个类,调用顺序为派生表从左到右顺序。
-
如果该对象需要虚函数指针(vptr),则该指针会被设置从而指向对应的虚函数表(vtbl)。
-
成员类对象的构造函数:如果类的变量中包含其他类(类的组合),需要在调用本类构造函数前先调用成员类对象的构造函数,调用顺序遵照在类中被声明的顺序。
-
派生类的构造函数。
-
析构函数与之相反。
-
构造的时候先调用基类的构造函数,再调用派生类的构造函数
析构的时候先调用派生类的析构函数,再调用基类的析构函数
-
拷贝构造函数中深拷贝和浅拷贝区别?
-
深拷贝时,当被拷贝对象存在动态分配的存储空间时,需要先动态申请一块存储空间,然后逐字节拷贝内容。
-
浅拷贝仅仅是拷贝指针字面值。
-
当使用浅拷贝时,如果原来的对象调用析构函数释放掉指针所指向的数据,则会产生空悬指针。因为所指向的内存空间已经被释放了。
-
-
拷贝构造函数和赋值运算符重载的区别?
-
拷贝构造函数是函数,赋值运算符是运算符重载。
-
拷贝构造函数会生成新的类对象,赋值运算符不能。
-
拷贝构造函数是直接构造一个新的类对象,所以在初始化对象前不需要检查源对象和新建对象是否相同;赋值运算符需要上述操作并提供两套不同的复制策略,另外赋值运算符中如果原来的对象有内存分配则需要先把内存释放掉。
-
形参传递是调用拷贝构造函数(调用的被赋值对象的拷贝构造函数),但并不是所有出现"="的地方都是使用赋值运算符,如下:
Student s; Student s1 = s; // 调用拷贝构造函数 Student s2; s2 = s; // 赋值运算符操作
注:类中有指针变量时要重写析构函数、拷贝构造函数和赋值运算符
-
-
虚函数和纯虚函数区别?
-
虚函数是为了实现动态编联产生的,目的是通过基类类型的指针指向不同对象时,自动调用相应的、和基类同名的函数(使用同一种调用形式,既能调用派生类又能调用基类的同名函数)。虚函数需要在基类中加上virtual修饰符修饰,因为virtual会被隐式继承,所以子类中相同函数都是虚函数。当一个成员函数被声明为虚函数之后,其派生类中同名函数自动成为虚函数,在派生类中重新定义此函数时要求函数名、返回值类型、参数个数和类型全部与基类函数相同。
-
纯虚函数只是相当于一个接口名,但含有纯虚函数的类不能够实例化。(抽象基类,类似于Java中的接口)
-
-
覆盖、重载和隐藏的区别?
-
覆盖是派生类中重新定义的函数,其函数名、参数列表(个数、类型和顺序)、返回值类型和父类完全相同,只有函数体有区别。派生类虽然继承了基类的同名函数,但用派生类对象调用该函数时会根据对象类型调用相应的函数。覆盖只能发生在类的成员函数中。(运行时多态)
-
隐藏是指派生类函数屏蔽了与其同名的函数,这里仅要求基类和派生类函数同名即可。其他状态同覆盖。可以说隐藏比覆盖涵盖的范围更宽泛,毕竟参数不加限定。
-
重载是具有相同函数名但参数列表不同(个数、类型或顺序)的两个函数(不关心返回值),当调用函数时根据传递的参数列表来确定具体调用哪个函数。重载可以是同一个类的成员函数也可以是类外函数。(编译时多态)
-
-
在main执行之前执行的代码可能是什么?
- 全局对象的构造函数。
-
哪几种情况必须用到初始化成员列表?
-
初始化一个const成员。
-
初始化一个reference成员。
-
调用一个基类的构造函数,而该函数有一组参数。
-
调用一个数据成员对象的构造函数,而该函数有一组参数。
-
-
什么是虚指针?
-
虚指针或虚函数指针是虚函数的实现细节。
-
虚指针指向虚表结构。
-
-
重载和函数模板的区别?
-
重载需要多个函数,这些函数彼此之间函数名相同,但参数列表中参数数量和类型不同。在区分各个重载函数时我们并不关心函数体。
-
模板函数是一个通用函数,函数的类型和形参不直接指定而用泛型来代表。但只适用于参数个数相同而类型不同的函数。
-
-
this指针是什么?
-
this指针是类的指针,指向对象的首地址。
-
this指针只能在成员函数中使用,在全局函数、静态成员函数中都不能用this。
-
this指针只有在成员函数中才有定义,且存储位置会因编译器不同有不同存储位置。
-
-
类模板是什么?(STL就是一种实现)
-
用于解决多个功能相同、数据类型不同的类需要重复定义的问题。
-
在建立类时候使用template及任意类型标识符T,之后在建立类对象时,会指定实际的类型,这样才会是一个实际的对象。
-
类模板是对一批仅数据成员类型不同的类的抽象,只要为这一批类创建一个类模板,即给出一套程序代码,就可以用来生成具体的类。
-
-
构造函数和析构函数调用时机?
-
全局范围中的对象:构造函数在所有函数调用之前执行,在主函数执行完调用析构函数。
-
局部自动对象:建立对象时调用构造函数,离开作用域时调用析构函数。
-
动态分配的对象:建立对象时调用构造函数,调用释放时调用析构函数。
-
静态局部变量对象:建立时调用一次构造函数,主函数结束时调用析构函数。
-