C++面试最常见问题(二)

一、重载、重写和重定义的区别?

重载:指的是在同一个作用域内,两函数的函数名可以相同,但是参数不能完全相同,可以是参数类型不同、参数个数不同或参数顺序不同,返回值可以相同也可以不同,virtual 关键字也是可有可无。根据参数列表决定调⽤哪个函数。

重写(也称覆盖):指的是在继承关系中(子类和父类),子类重新定义⽗类中除了函数体外完全相同的虚函数。但需要注意被重写的函数不能是 static 的,⼀定要是虚函数,且其他⼀定要完全相同。重写函数的访问修饰符是可以不同的,尽管 virtual 中是 private 的,派
⽣类中重写可以改为 public。

重定义:派⽣类重新定义⽗类中相同名字的⾮ virtual 函数,参数列表
和返回类型都可以不同,即⽗类中除了定义成 virtual 且完全相同的同名函数才不会被派⽣类中的同名函数所隐藏(重定义)。
即在继承关系中,子类实现了一个和父类名字一样的函数,(只关注函数名,和参数与返回值无关)这样的话子类的函数就把父类的同名函数隐藏了。

二、c++中的内存四区

运行程序前:
代码区:存放函数体的二进制代码,由操作系统进行管理(注释不算)。代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中又一份代码即可。代码区是只读的,使其只读的原因使防止程序意外的修改了它的指令。
全局区:存放全局变量和静态变量以及常量。

程序运行后:
栈区: 由编译器自动分配释放,存放函数的参数值,局部变量等。
堆区: 由程序员分配和释放(new/delete),如果程序员不释放,程序结束时由操作系统回收。

内存四区的意义:
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。

三、一个数据成员是否可以既是const又是static,如果不行,请说出原因。

答:一个数据成员可以既是const又是static,表示为静态常量。
原因:
(1)静态成员一般在类外初始化。
(2)常量一般在构造函数后初始化。
(3)静态常量在类外初始化,但要在类外初始化的同时声明为const。

四、构造函数的作用和特点?

作用:
用来在创建对象时,对对象进行初始化。

特点:
(1)构造函数是成员函数,函数可写在类内,也可写在类外。
(2)构造函数是一个特殊的函数,该函数的名字与类名相同,该函数不指定类型说明,无返回值。
(3)构造函数可以重载。
(4)程序中不能直接调用构造函数,在创建对象时系统自动调用构造函数。
(5)不用来初始化static数据成员,因为static数据成员不属于类.
(6)如果没有在类中给出构造函数,编译器自动生成一个默认的构造函数(无参数、函数体为空),当在类中手动给出构造函数时,默认的构造函数消失。

使用注意事项:
当类中存在内置类型或复合类型(比如数组和指针)或管理动态内存的类时,必须手动定义构造函数,不能使用默认的构造函数。否则,用户创建的类的对象就会得到未定义的值。

五、析构函数的作用和特点?

作用:
用来对对象进行指针释放和内存释放等操作。

特点:
(1)析构函数也是特殊的类成员函数,它没有返回类型、没有参数、没有重载。
(2)一个类中只能定义一个析构函数,析构函数不能重载。
(3)析构函数必须存在于public中。
(4)析构函数可以被调用,也可以由系统调用。在下面两种情况下,析构函数会被自动调用。一是如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用;二是当一个对象是使用new运算符被动创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。
(6)如果没有在类中给出析构函数,编译器自动生成一个默认的析构函数(无参数、函数体为空)。当手动给出析构函数时,默认的消失。

使用注意事项:
当类中有动态内存的变量时,不能使用默认析构函数,必须手动设计析构函数。

六、const关键字的作用?

(1)定义常量:const修饰的类型为type的变量value是不可变的。
(2)修饰指针:可以指定指针本身为const,变成指针常量,指针的指向不可以改变,指针存放的值可以改;也可指定指针所指的数据为const,变成常量指针,指针的指向可以改,指针的值不可以改;或二者同时指定为const,指针的指向和值都不可以修改。
(3)const修饰类的成员变量,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。
(4)const修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰。
(5) 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值。

七、static关键字的作用?

(1)隐藏。当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
(2)保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围。
(3)static的第三个作用是默认初始化为0。
(4)在类中的static成员变量属于整个类拥有,对类的对象只有一份拷贝。
(5)在类中的static成员函数属于整个类拥有,这个函数不接收this指针,因而只能访问static成员变量。

static全局变量与普通的全局变量的区别
 这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
 
static局部变量和普通局部变量的区别
把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。 static局部变量只被初始化一次,下一次依据上一次结果值。

static函数与普通函数的区别
   static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static修饰的函数),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。

八、构造函数可以是虚函数么?析构函数呢可以是虚函数么?

对于构造函数而言:
(1)当我们在类中定义了虚函数就会有一个虚函数表 vtable,vtable 存储于对象的内存空间中,通过虚指针 vptr来确认调用哪一个函数。如果构造函数是虚的,那么就需要通过 vtable来调用,但是此时对象还未实例化,即内存空间还没有,是无法找到vtable的。
(2)虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数就没有实际意义。
(3)在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数),而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,没有必要成为虚函数;
综上所述:构造函数不能是虚函数,因为创建一个对象时我们总是要明确指定对象的类型。

对于析构函数而言:
析构函数可以为虚函数,而且当使用父类指针或引用来调用子类时,最好将父类的析构函数声明为虚函数,否则可能存在内存泄露的问题。

九、哪些函数不能定义为虚函数

1.友元函数,它不是类的成员函数。
2.全局函数。
3.静态成员函数,它没有this指针。
4.构造函数,拷贝构造函数,以及赋值运算符重载(可以但是一般不建议作为虚函数)。

十、虚函数表存放的内容是什么?虚表指针存放在哪里?

1.虚函数表存放的内容:类的虚函数的地址。
2.虚函数表建立的时间:编译阶段,即程序的编译过程中会将虚函数的地址放在虚函数表中。
3.虚表指针保存的位置:虚表指针存放在对象的内存空间中最前面的位置,这是为了保证正确取到虚函数的偏移量。

猜你喜欢

转载自blog.csdn.net/qq_46901210/article/details/124190346