C++或Java、C#等面向对象语言基础面试题
前言
计算机语言的发展经历了机器语言、面向过程语言、以及面向对象语言,机器语言晦涩难懂高效,代表语言是汇编语言,面向过程语言代表C语言,面向对象语言代表C++和Java。由于在计算机发展早期,硬件设备极其有限,效率往往是第一要素,汇编语言语言以及后来的C语言就作为软件开发必修内容,但是随着硬件和计算机语言发展到今天,一个以对象管理为概念语言就孕育而生。
C++语言以及后来的Java语言就流行起来,面向对象语言的理解以正常社会个体来理解程序语言本身,符合人的正常思考思维和人性思维,同时对于大系统以及架构维护比先前的面向过程语言容易许多。
在理解面向对象语言时,对于面向对象语言三大特性是什么就比较重要。
一、面向对象(OOP)三大特性
1)封装、继承、多态
二、封装
对象的属性(成员变量)和操作或服务(函数)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。类(class)就是封装的总体体现, public、protect、private成员属性的访问权限让开发者设定哪些细节可以暴露,哪些需要隐藏。 我们定义Person类,如下:
class Person
{
private int age;
private string name;
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
public void setName(string name)
{
this.name = name;
}
public string getName()
{
return name;
}
}
对于 Person类有两个成员变量,age 和 name ,我们不需要直接对他进行访问,更改需要使用setAge接口,访问需要getAge(),这样保证数据的隐秘性成员变量不直接暴露。当然我们也可以定义 public int age,但是这不符合封装性的设计思想。
如何体现封装性:
1)类(class)
体现了封装性的完整性
2)通过public,protect、private访问权限修饰
它们控制想要访问的细节,哪些接口和成员变量需要被影藏,修改成员变量的接
口 setxxx()进行修改,getxxx()进行访问
3)良好的接口设计
应该只关注传入参数和返回值以及其作用,并不需要关注过多细节。
三、继承
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类
class Base Character
{
private int hp;
private int mp;
public void setHp(int hp)
{
this.hp = hp;
}
public int getHp()
{
return hp;
}
public void setMp(int mp)
{
this.mp = mp;
}
public int getMp()
{
return mp;
}
}
class Hero public BaseCharacter
{
//当前任务ID
private int currentTaskId;
public void setCurrentTaskId(int id)
{
this.currentTaskId = id;
}
public int getCurrentTaskId()
{
return this.currentTaskId;
}
省略 ...
}
BaseCharater* p = new Hero();
hero类继承BaseCharacter类,hero和BaseCharacter都有hp和mp,这样hero就必重写,同时实现了代码设计的统一,可以通过统一接口i通过基类访问派生类,同时派生类有着基类所有属性。派生类是基类的扩展,基类的私有成员变量也会被派生类继承,只不过派生类不能访问罢了。
继承让代码更简洁,但是继承往往打破的封装性,程序设计往往追求高内聚、低耦合,继承是一种高耦合的设计,所以在代码设计时需要做出取舍。
四、多态
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
多态体现:
多态最直接的体现virtual 虚函数和纯虚函数
如下有:战斗角色类FightCharacter,它的子类都有attack攻击行为,但是attack攻击行为做执行的细节有很大的不同。我们定义为纯虚函数
同时有每帧更新行为,子类可能与父类的更新内容一样,也可能不一样,所以我们把他定义为虚函数,
class FightCharacter public BaseCharacter
{
//攻击行为
public virtual void attack(int destId)=0;
public virtual void update()
{
省略 ...
}
}
class Hero public FightCharacter
{
省略 ...
//英雄攻击时有自己的逻辑
public override void attack(int destId)
{
省略 ...
}
public override void update()
{
省略 ...
}
省略 ...
}
class Monster public FightCharacter
{
省略 ...
//怪物攻击时有自己的逻辑
public override void attack(int destId)
{
省略 ...
}
public override void update()
{
省略 ...
}
省略 ...
}
FightCharacter* pHero = new Hero();
FightCharacter* pMonster = new Monster ();
pHero->attack(); //调用的是pHero实际指向对象所定义的类的attack函数
pHero->update();//调用的是pHero实际指向对象所定义的类的update函数
pMonster->attack();//调用的是pMonster实际指向对象所定义的类的attack函数
pMonster->update();//调用的是pMonster实际指向对象所定义的类的update函数
在什么时候定义虚函数,在什么时候定义纯虚函数
如果子类都有某一行为,但是各个子类行为实现细节没有统一性,那么就定义纯虚函数
如果子类中都有某一行为,但是各个子类有些许差异,这时候应该定义为虚函数。
总结
面向对象的三大特性:封装、继承、多态
封装的体现:类和public、protect、private 访问权限,良好的接口设计
继承:通过派生子类,来复用父类的属性和成员,提高代码的复用率与代码简洁、易维护,继承是高耦合设计,打破封装性,要有选择性使用,把握分寸。
多态的体现:虚函数与纯虚函数