目录
2.在头文件中进行类的声明,在对应的实现文件中进行类的定义有什么意义?
5.通过成员访问限定符将类的成员划分不同访问级别有什么好处?
16.说明C和C++中的struct和class有什么区别?
23.对于用new动态创建的对象实例,如果不用delete销毁会怎么样?
24.成员函数通过什么来区分不同对象的数据成员?为什么它能够区分?
1.谈谈你对面向对象的认识。
面向对象是一种自下而上的程序设计方法,不像过程式设计那样一开始就要用main概括出整个程序(过程式设计的思路是功能分解、逐步求精),面向对象设计往往从问题的一部分着手,一点一点地构建出整个程序。面向对象设计以数据为中心,类作为表现数据的工具是划分程序的基本单位,类封装了数据,类的成员函数作为其对外的接口抽象地描述了类。用类将数据和操作这些数据的函数放在一起,可以说这就是面向对象设计方法的本质。面向对象的主要概念包括类与对象、继承和多态等。
传统的结构化程序设计多是基于功能的思想进行考虑和设计的,而面向对象的程序设计则是基于对象的角度考虑问题的,这样做能够使得程序更加简洁和清晰。
2.在头文件中进行类的声明,在对应的实现文件中进行类的定义有什么意义?
这样可以提高编译效率,因为如果分开只需要编译一次生成对应的.obj文件,然后在应用该类的地方这个类就不会被再次编译,从而大大提高了编译效率,另外使得程序结构更清晰。
3.类定义?
通常将类声明和类实现合起来称类定义。
类声明仅告诉编译器有一个指定名称的类,它具有哪些数据成员、哪些成员函数,并没有为数据成员和成员函数分配内存空间。
当遇到类定义时,C++编译器会为静态数据成员分配内存空间并初始化(如果有),但不会为其他数据成员分配内存空间,同时会为成员函数分配代码空间,这样每个成员函数都有一个存储其代码的地址。在进入链接阶段时将通过对象调用成员函数的地方用对应函数成员的地址替代,并传递当前对象的指针和相关参数。
4.类成员访问权限?
private:声明私有成员。私有数据成员只允许在类中访问,私有成员函数只允许被类中的其他成员函数调用。在类外不允许访问私有数据成员,也不允许调用私有成员函数;
public:声明共有成员。公有数据成员允许在类中或类外(通过类对象)访问,公有成员函数允许在类中或类外(通过类对象)调用;
protected:声明保护成员。保护数据成员只允许在类中或者子类中访问,保护成员函数允许在类中或者子类中调用。在类外不能访问该类的保护数据成员,也不能调用该类的保护成员函数。
默认情况下,一个类中的成员是私有的。
5.通过成员访问限定符将类的成员划分不同访问级别有什么好处?
首先是信息隐蔽,即实现的封装,将类的内部实现和外部接口分开,这样使用该类的程序不需要了解类的详细实现;
其次是数据保护,即将类的重要信息保护起来,以免其他程序不恰当地修改。
6.什么是内联函数?使用内联函数有什么好处?
类的成员函数用于实现某种操作,成员函数的定义体可以放在类声明体中,也可以放在类声明体外。在类声明体中实现的函数称为内联函数,在类声明体外实现的函数可以通过在函数声明和定义时加上inline来表示该函数是内联函数,否则不是内联函数。实际上,普通函数(非类的成员函数)也可以加上inline变成内联函数。
C++编译器在遇到调用内联函数的地方会用函数体中的代码来替换函数调用,好处是节省函数调用带来的参数传递、栈空间的进栈与出栈等开销,从而提高执行速度,但付出的代价是增加了代码长度。
7.使用内联函数有哪些注意事项?
使用inline关键字的函数不一定都会被编译器在调用处展开,inline对于编译器只是一个建议而已,因为把一个函数声明为inline函数并不一定真正适合在调用点上展开。
通常只将较短的函数(如1-5行的小函数)设计成内联函数,如果函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。
在内联函数内不允许有循环语句和开关语句。如果内联函数有这些语句,则编译器将该函数视为普通函数。
递归函数不能被用来做内联函数,因为无法在调用点上完全展开。
内联函数的定义必须出现在内联函数第一次被调用之前。
对于同一个程序的不同源程序文件,如果inline函数出现,其定义必须相同。如果两个定义不同,程序会产生不确定的行为。建议把inline函数的定义放在头文件中,在每个调用该inline函数的源程序文件中包含该头文件。
在执行程序时内联函数已经不存在了,那么在程序中能不能取一个内联函数的地址呢?是可以的,在这种情况下编译器为此生成一个函数体。
8.内联函数和宏定义有什么区别?
内联函数一般实现较小的功能,可以采用宏定义达到相同的目的,但二者是有区别的:
内联函数在执行时可调试,而宏定义不可以。
编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义不会。
内联函数可以访问类的成员变量,而宏定义不能。
在类中声明同时定义的成员函数自动转化为内联函数。
9.构造函数有什么性质?
构造函数的名称和类名相同;
构造函数尽管是一个成员函数,但没有任何函数类型,也不属于void函数;
一个类除了不带参数的默认构造函数外,还可以设计一个或多个带参数的重载构造函数;
如果一个类中没有定义任何构造函数,编译器会自动生成一个不带参数的默认构造函数(函数体为空)。
如果一个类中声明有任何带参数的构造函数,编译器不会自动生成不带参数的默认构造函数。
10.什么是拷贝构造函数?
拷贝构造函数(复制构造函数)是另外一种特殊的构造函数,具有一般构造函数的所有特性,主要用于通过一个已经存在的对象创建一个新的对象时实现对象之间数据成员的复制操作。
每个类都必须有一个拷贝构造函数,如果没有生命,编译器自动生成一个默认的拷贝构造函数,以实现数据成员的简单复制(浅复制)。
拷贝构造函数必须采用引用型参数,而不是传值参数,否则拷贝构造函数将产生无限递归,因为传值调用时必须建立传递给拷贝构造函数的对象副本,这样又要调用拷贝构造函数。而引用型参数不需要调用拷贝构造函数。
11.什么时候拷贝构造函数会自动被调用?
当用类的一个对象去初始化该类的另一个对象时;
如果函数的形参是非引用类对象,调用函数进行形参和实参结合时;
如果函数的返回值是非引用类对象,函数调用完成返回时。
12.析构函数有什么性质?
析构函数在类对象销毁时自动执行,用于对象内存的清理工作等。
一个类只能有一个析构函数,而且析构函数没有参数。
析构函数的名称是“~”加上类的名称。
析构函数也没有任何函数类型,不能像普通成员函数那样显式调用。
如果一个类没有声明析构函数,编译器会自动生成一个函数体为空的默认析构函数。
13.类对象的存储空间分配方式?
一个类对象的分配空间中仅包含所有非静态数据成员,并按这些数据成员定义的顺序存放。
一个类对象的大小为其所有非静态数据成员的类型大小之和。普通成员函数与sizeof无关。(当类中有一个或多个虚函数时,由于要维护虚函数表,所以有一个虚函数表指针。另外当存在虚继承时还需要一个指向虚基类的指针,每个指针占用一个地址大小的内存空间,即4个字节)。
类对象中的各个数据成员分配内存空间时遵守内存对其规则。
14.什么是空类?系统会为空类添加哪些成员函数?
空类指不包含任何数据成员和函数成员的类。
空类Empty默认产生的类成员函数如下:
class Empty
{
public:
Empty(); //默认的构造函数
Empty(const Empty& ); //默认的拷贝构造函数
~Empty(); //默认的析构函数
Empty& operator=(const Empty &); //默认的赋值运算符
Empty* opetator&(); //默认的取址运算符
const Empty* operator&() const; //默认的const取址运算符
}
15.对于空类A,为什么sizeof(A)=1?
因为类是用来定义对象的,每个对象的存储空间中存储相应的数据成员值,通常第一个数据成员的地址为对象地址。如果长度为0,就无法实例化空类的对象。
为此编译器添加一个字节的数据成员,实际上对于不含任何数据成员的类,编译器都是如此。
16.说明C和C++中的struct和class有什么区别?
C和C++中的struct有区别,C中的struct不能有成员函数,而C++中的struct可以。
C++中的struct和class的主要区别为成员的默认访问权限不同,struct成员的默认权限为public,而class成员的默认权限为private。
17.静态成员包括什么?为什么提出静态成员?
静态成员包括静态数据成员和静态成员函数。
提出静态成员的目的是为了解决数据共享的问题。
18.静态数据成员?
静态数据成员是类中所有对象共享的成员,而不是某个对象的成员,也就是说,静态数据成员的存储空间不是放在每个对象中,而是和成员函数一样放在类的公共区中,所以有时将静态数据成员称为类变量。
19.类中静态数据成员有什么特点?
静态数据成员是类成员,无论定义多少个类对象,静态数据成员在程序中只保留一份。
静态数据成员存储在全局数据区,为本类的所有对象共享,不属于特定的类对象。
静态数据成员必须在类外进行初始化。
20.静态成员函数?
静态成员函数与静态数据成员类似,也是从属于类的。只要类存在,静态成员函数就可以使用,静态成员函数的声明需要使用static关键字。
静态成员函数只能访问静态数据成员,不能直接访问类中的非静态数据成员,因为非静态数据成员只有对象存在时才有意义,可以通过类对象来访问类的成员。
21.说明静态成员函数存在的意义?
静态成员函数只能操作静态数据成员,所以使用静态成员函数仅仅可以实现操作静态数据的功能,如静态私有成员在类外不能被访问,可通过类的静态成员函数来访问。
22.静态对象?
静态对象和普通静态变量一样,其生存期是整个程序,其作用域是所在的函数。
23.对于用new动态创建的对象实例,如果不用delete销毁会怎么样?
对于用new运算符动态创建的对象实例,可以使用delete运算符销毁,在销毁时会自动调用析构函数。
若不使用delete销毁,在程序结束时该对象实例仍然存在,并占用相应的存储空间,即系统不能自动销毁动态创建的对象实例。
24.成员函数通过什么来区分不同对象的数据成员?为什么它能够区分?
类的成员函数只存放一份,其入口参数有一个当前对象的地址,即this指针。
通过this指针指向当前对象,所以成员函数通过this指针来区分不同对象的数据成员,因为不同的类对象是单独存放的,其中包含数据成员值。
25.创建对象和销毁对象的顺序是什么样的?
当程序中创建各类对象时按对象的顺序调用构造函数。
程序中对象的销毁顺序如下:
用new创建的多个实例按delete的顺序依次销毁;
定义的多个局部子对象按创建子对象相反的顺序销毁;
定义的多个静态局部子对象按创建子对象的相反顺序销毁;
定义的多个全局对象(含静态全局对象)按创建对象的相反顺序销毁。
所以,当程序创建有多种类别的对象时析构函数的调用顺序并不一定正好和构造函数的调用顺序相反。
26.什么是this指针?
this是一个隐含于每一个类对象的特殊指针,该指针值是一个正在被某个成员函数操作的对象的地址。
27.说明赋值运算符=和拷贝构造函数的区别和联系?
赋值运算符和拷贝构造函数的相同点是两者都将一个对象的数据成员复制到另一个中。
两者的不同点是拷贝构造函数涉及新建一个对象,而赋值运算符被复制的对象已创建。
28.什么是浅复制,什么是深复制?
当两个对象之间进行复制时,若复制完成后它们还共享某些资源(内存空间),其中一个对象的销毁会影响另一个对象,这种对象之间的复制称为浅复制。
当两个对象之间进行复制时,若复制完成后它们不会共享任何资源(内存空间),其中一个对象的销毁不会影响另一个对象,这种对象之间的复制称为深复制。
29.什么时候必须重写拷贝构造函数?
当构造函数涉及动态存储分配空间时要自己写拷贝构造函数,并且要深复制。
30.简要说明类与对象有什么区别?
类是某一类事物的一般性的集合体,是相同或相似的各个事物共同特征的一种抽象。对象是类的实例(instance)。
对象与类的关系就像变量与数据类型的关系一样。
31.类的核心特性有哪些?
类具有封装性、继承性、多态性。
类的封装性为类的成员提供公有、保护和私有等多级访问权限,目的是隐藏类中私有成员的实现细节。
类的继承性提供从已存在的类创建新类的机制,继承使一个新类自动拥有父类的全部成员。
类的多态性提供类中方法执行的多样性,多态性有重载和重写两种表现形式。
参考:《直击招聘 程序员面试笔试 C++语言深度解析》李春葆、李筱池 主编