c++ 面向对象的三大特性详解—封装、继承、多态

面向对象:对象是指具体的某一个事物,这些事物的抽象就是类,类中包含数据(成员变量)和动作(成员方法)。

面向对象的三大特性:

封装: 将具体的实现过程和数据封装成一个函数,只能通过接口进行访问,降低耦合性。
继承: 子类继承父类的特征和行为,子类有父类的非 private 方法或成员变量,子类可以对父类的方法进行重写,增强了类之间的耦合性,但是当父类中的成员变量、成员函数或者类本身被 final 关键字修饰时,修饰的类不能继承,修饰的成员不能重写或修改。
多态: 多态就是不同继承类的对象,对同一消息做出不同的响应,基类的指针指向或绑定到派生类的对象,使得基类指针呈现不同的表现方式。

封装可以使得代码模块化,继承可以扩展已经存在的代码,目的都是为了代码重用。多态的目的是为了接口重用。

1、继承

一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

1
2
class child: public parent

其中public可以替换为protectprivate

类型决定了子类将会把基类的内容继承为什么类型。

1.1 继承类型

我们几乎不使用 protectedprivate 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有保护成员来访问。

  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有保护成员将成为派生类的保护成员。

  • 私有继承(private):当一个类派生自私有基类时,基类的公有保护成员将成为派生类的私有成员。


1.2访问权限总结:

因此,如果基类成员不想被派生类的成员函数访问,应该在基类中声明为private。

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。

  • 基类的重载运算符。

  • 基类的友元函数。


1.3多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。

C++ 类可以从多个类继承成员,语法如下:

1
2
3
4
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};

2、多态

C++多态(polymorphism)是通过虚函数来实现的.

当类存在多层的层次结构,并且类之间是通过继承关联的时,C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

📌简单来说就是一个基类被派生为多个子类,子类中对基类中的函数进行了重写。我们希望利用子类的指针或者引用可以实现调用重写的函数,而不是基类中原本的函数,这时候,我们需要对基类中和子类中同名的函数前加上关键字virtual。这里我们应该称为是函数的重写。

虚函数是类方法中的一种特殊函数,当你调用它时,它会匹配派生最远的重写版本。这种特性是多态性。匹配的规则是相同的函数签名(函数名,参数个数与类型)以及返回类型(返回类型可以不相同,但必须存在派生关系)。虚函数仅需要再前面加上一个virtual关键字即可.

论是基类版本还是派生类版本,我们都在函数前面使用了virtual关键字,事实上,派生类中的virtual关键字并不是必要的。一旦基类中的方法打上了virtual标签,那么派生类中匹配的函数也是虚函数。但是,还是建议在后面的派生类中加上virtual关键字,作为虚函数的一种提醒,以便后面可能还会有更远的派生。

到底什么时候使用虚函数?大部分时候,我们希望派生类是真正的“重写”基类函数,而不是“隐藏”。所以一般建议将所有方法都声明为virtual。既然如此,为什么编译器不默认这样做呢,其实对于Java语言来说,所有的方法默认是虚函数。但是使用虚函数是有代价的,相对于普通函数,虚函数的调用代价稍高,但是这种差别不会太大,所以还是建议所有方法都使用virtual关键字

析构函数要声明为虚函数

对于析构函数,大部分时间我们只需要使用编译器提供的默认版本就好,除非涉及到释放动态分配的内存。但是如果存在继承,虚函数最好声明为虚函数。否则删除一个实际指向派生类的基类指针,只会调用基类的析构函数,而不会调用派生类的析构函数以及派生类数据成员的析构函数。 这样就可能造成内存泄露。

参考文献:

https://zhuanlan.zhihu.com/p/54145222