班图技术面试总结

班图网络科技面试总结:

1、什么是多态,有什么功能?

答:多态性是指在一般类中定义的属性或服务被特殊类继承之后,可以具有不同的数据类型或者表现出不同的行为,所以能从一个类派生出另一个类就是多态性的基本条件。
从实现角度来讲,多态性可以划分为两类:编译时的多态和执行时的多态。编译时的多态是指编译器在编译时确定同名操作的具体操作对象,在C++中主要是通过函数重载和运算符重载实现的;而执行时的多态是指程序运行时确定具体的操作对象,C++主要通过虚函数来实现。编译时的多态也称静态绑定,运行时的多态也称动态绑定。
实现多态有两种方式:重载和覆盖。

2、重载和覆盖:

  • 重载:
    重载是多态性的最简形式,分为运算符重载和函数重载。重载是指允许存在多个同名函数,而这些函数的参数表不同(或者参数个数不同,或者参数类型不同,或者两者都不同)。重载的实现是:编译器根据函数不同的参数表,对
    同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。对于函数的调用,在编译期间就已经确定了,是静态的。也就是说,他们的地址在编译期间就被绑定了。

  • 覆盖
    覆盖是指派生类重新定义基类的虚函数的做法。当派生类重新定义了基类的虚函数后,基类指针根据赋给它的不同的派生类指针,动态的调用属于派生类的同名函数,这样的函数调用在编译期间是无法确定的(调用的派生类的虚函数地址无法给出)。因此,这样的函数地址是在运行期间绑定的。

3、虚函数和纯虚函数

  • 虚函数:
    虚函数就是在类中用virtual声明的成员函数,基类的虚函数在派生类中仍然是虚函数。一个函数一旦被说明为虚函数,则无论它被继承多少层,在每一层派生类中该函数都保持virtual特性。虚函数是在运行时基于指针所指向对象的类型进行选择的,而不是指针的类型。如果派生类中缺少对应得虚函数的定义,将默认使用基类的虚函数。
    作用:可以在派生类中重新定义虚函数,使得指向基类对象的指针在指向派生类对象后,能够访问派生类中重新定义的虚函数。
    注意:在派生类重新定义时,其函数名、参数个数、参数类型的顺序及返回值,都必须与基类中的完全相同。
    访问方式:用基类指针访问或用对象名访问

  • 纯虚函数:
    是一种特殊的虚函数,声明格式为:

    virtual 类型 函数名(参数表)= 0;

纯虚函数不能直接被调用,它仅提供一个与派生类相一致的接口,用于存放派生类中具有相同名字的函数。纯虚函数不能被继承。

4、多重继承:
1.多重继承的构造次序:基类构造函数按照基类构造函数在类派生列表中的出现次序调用,构造函数调用次序既不受构造函数初始化列表中出现的基类的影响,也不受基类在构造函数初始化列表中的出现次序的影响。

2.在单个基类情况下,派生类的指针或引用可以自动转换为基类的指针或引用,对于多重继承也是如此,派生类的指针或引用可以转换为其任意基类的指针或引用。

3.多重继承像单继承一样,用基类的指针或引用只能访问基类中定义(或继承)的成员,不能访问派生类中引入的成员。
当一个类继承于多个基类的时候,那些基类之间没有隐含的关系,不允许使用一个基类的指针访问其他基类的成员。

4.如果用户自己定义了派生类自己的复制构造函数或赋值操作符,则用户要负责复制(赋值)所有的基类子部分。而对于编译器默认合成的派生类的复制构造函数或赋值操作符会自动复制或赋值基类部分。

5.在多重继承下,通常成员函数中使用的名字的查找首先在函数本身进行,如果不能在本地找到名字,就继续在成员的类中查找,然后依次查找每个基类。注意名字查找总是按照两个步骤进行的:(1)首先编译器找到一个匹配的声明(与参数、返回值、公有私有均没有关系,只与名字有关) (2)然后,编译器才确定所找到的声明是否合法。

5、申请内存时,堆内存和栈内存的区别

  • 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量
    的存储区。里面的变量通常是局部变量、函数参数等。
  • 堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应
    用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,
    那么在程序结束后,操作系统会自动回收。

6、结构体和类的区别
C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。
struct能包含成员函数吗? 能!
struct能继承吗? 能!!
struct能实现多态吗? 能!!!

既然这些它都能实现,那它和class还能有什么区别?

最本质的一个区别就是默认的访问控制,体现在两个方面:

1)默认的继承访问权限。struct是public的,class是private的。
你可以写如下的代码:
struct A
{
char a;
};
struct B : A
{
char b;
};

这个时候B是public继承A的。

如果都将上面的struct改成class,那么B是private继承A的。这就是默认的继承访问权限。

所以我们在平时写类继承的时候,通常会这样写:
struct B : public A

就是为了指明是public继承,而不是用默认的private继承。

当然,到底默认是public继承还是private继承,取决于子类而不是基类。

我的意思是,struct可以继承class,同样class也可以继承struct,那么默认的继承访问权限是看子类到底是用的struct还是class。如下:

struct A{};class B : A{}; //private继承
struct C : B{}; //public继承

2)struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。

注意我上面的用词,我依旧强调struct是一种数据结构的实现体,虽然它是可以像class一样的用。我依旧将struct里的变量叫数据,class内的变量叫成员,虽然它们并无区别。

其实,到底是用struct还是class,完全看个人的喜好,你可以将你程序里所有的class全部替换成struct,它依旧可以很正常的运行。但我给出的最好建议,还是:当你觉得你要做的更像是一种数据结构的话,那么用struct,如果你要做的更像是一种对象的话,那么用class。

当然,我在这里还要强调一点的就是,对于访问控制,应该在程序里明确的指出,而不是依靠默认,这是一个良好的习惯,也让你的代码更具可读性。

说到这里,很多了解的人或许都认为这个话题可以结束了,因为他们知道struct和class的“唯一”区别就是访问控制。很多文献上也确实只提到这一个区别。

但我上面却没有用“唯一”,而是说的“最本质”,那是因为,它们确实还有另一个区别,虽然那个区别我们平时可能很少涉及。那就是:“class”这个关键字还用于定义模板参数,就像“typename”。但关键字“struct”不用于定义模板参数。这一点在Stanley B.Lippman写的Inside the C++ Object Model有过说明。

问题讨论到这里,基本上应该可以结束了。但有人曾说过,他还发现过其他的“区别”,那么,让我们来看看,这到底是不是又一个区别。还是上面所说的,C++中的struct是对C中的struct的扩充,既然是扩充,那么它就要兼容过去C中struct应有的所有特性。例如你可以这样写:

struct A //定义一个struct
{
char c1;
int n2;
double db3;
};
A a={‘p’,7,3.1415926}; //定义时直接赋值

也就是说struct可以在定义的时候用{}赋初值。那么问题来了,class行不行呢?将上面的struct改成class,试试看。报错!噢~于是那人跳出来说,他又找到了一个区别。我们仔细看看,这真的又是一个区别吗?

你试着向上面的struct中加入一个构造函数(或虚函数),你会发现什么?
对,struct也不能用{}赋初值了。

的确,以{}的方式来赋初值,只是用一个初始化列表来对数据进行按顺序的初始化,如上面如果写成A a={‘p’,7};则c1,n2被初始化,而db3没有。这样简单的copy操作,只能发生在简单的数据结构上,而不应该放在对象上。加入一个构造函数或是一个虚函数会使struct更体现出一种对象的特性,而使此{}操作不再有效。

事实上,是因为加入这样的函数,使得类的内部结构发生了变化。而加入一个普通的成员函数呢?你会发现{}依旧可用。其实你可以将普通的函数理解成对数据结构的一种算法,这并不打破它数据结构的特性。

那么,看到这里,我们发现即使是struct想用{}来赋初值,它也必须满足很多的约束条件,这些条件实际上就是让struct更体现出一种数据机构而不是类的特性。

那为什么我们在上面仅仅将struct改成class,{}就不能用了呢?

其实问题恰巧是我们之前所讲的——访问控制!你看看,我们忘记了什么?对,将struct改成class的时候,访问控制由public变为private了,那当然就不能用{}来赋初值了。加上一个public,你会发现,class也是能用{}的,和struct毫无区别!!!

做个总结,从上面的区别,我们可以看出,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。

7、static、函数前加static的用处
在C语言中,关键字static有三个明显的作用:
1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。注意,只有在定义了变量后才能使用。如果变量定义在使用之后,要用extern 声明。所以,一般全局变量都会在文件的最开始处定义。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

在嵌入式系统中,作为程序员,我们要懂得移植的重要性,程序可能是很多程序员共同合作同时完成,在定义变量及函数时,可能会重名,这给系统的集成开发带来麻烦,因此保证不冲突的办法是表示此变量或函数是本地的,使用static即可。
在Linux的模块编程中,这一条很明显,所有的函数和全局变量都要用static关键字声明,将其作用域限制在本模块内部,与其他模块共享的函数或者变量要EXPORT到内核中。
static关键字有以下几个作用:
1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
以下是C++对static的扩展
4)在类中的static修饰的成员变量属于类可以共享,相当于全局变量,不属于具体的某个实例或者对象,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见,还有必须在类外初始化,static成员变量访问形式:1°可以被对象访问 2° 可以被类访问 3°可以被对象指针访问
5)普通函数有this指针,指针可访问所有成员,但在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,所以只能访问类的static成员变量。static只能在声明函数时加,不能再定义时候加(类中声明,类外初始化)

详细介绍参见:https://www.cnblogs.com/songdanzju/p/7422380.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值