代码示例:面向对象——封装、继承、多态(多态的四种类型)

面向对象——封装、继承、多态(多态的四种类型)

1. 封装

把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

  • public: 所有实体都可以访问
  • protected: 只允许本类(和子类)的成员函数访问
  • private: 只允许本类的成员函数+友元类或友元函数访问(友元类暂时还没学习)

2. 继承

派生类(子类) : 基类(父类)。

举例:cat类继承了Felid(猫科动物类)

class Felid {
public:
 
};

class Cat : public Felid {
public:
 
};

3. 多态

本模块主要参考了 The Four Polymorphisms in C++ (翻译+一点改动,英文描述可能更清晰,建议最好看原文,我这里主要是方便复习)

总结:多态就是一个函数 / 运算符… 的多种不同实现形态(同名,但是用法 / 效果不同)。

使用哪一个具体实现呢?(要根据参数,子类类型…来确定)

  • 运行时多态:运行时确定使用哪一个函数;

  • 编译时多态:编译时确定使用哪一个具体函数。

四种类型,分别是(后面分别有例子介绍):

  • 子类型多态(Subtype polymorphism,运行时多态)():虚函数;

  • 参数多态(Parametric polymorphism,编译时):类模板、函数模板;

  • 重载(Ad-hoc polymorphism / overloading,编译时):函数重载、运算符重载;

  • 强制多态(Coercion Polymorphism,编译 / 运行时)((implicit or explicit) casting):基本类型转换、自定义类型转换;

3.1 子类型(运行时)多态

代码示例:面向对象——封装、继承、多态(多态的四种类型)_第1张图片

猫科动物类:Felid。假定所有猫科动物都会叫:meow()

但是每种猫咪叫法不同,家猫cat, 老虎tiger, 豹猫Ocelot各有不同的叫声,因此Felid用虚函数virtual void meow() = 0;,而各自真实的叫声,在子类家猫cat, 老虎tiger, 豹猫Ocelot中具体实现

// file cats.h

class Felid {
public:
 virtual void meow() = 0;
};

class Cat : public Felid {
public:
 void meow() { std::cout << "Meowing like a regular cat! meow!\n"; }
};

class Tiger : public Felid {
public:
 void meow() { std::cout << "Meowing like a tiger! MREOWWW!\n"; }
};

class Ocelot : public Felid {
public:
 void meow() { std::cout << "Meowing like an ocelot! mews!\n"; }
};

猫科动物叫声调用如下

#include 
#include "cats.h"

void do_meowing(Felid *cat) {
 cat->meow();
}

int main() {
 Cat cat;
 Tiger tiger;
 Ocelot ocelot;

 do_meowing(&cat);
 do_meowing(&tiger);
 do_meowing(&ocelot);
}

因为cat, tigerocelot都是派生于Felid,因此都可以调用meow()成功,输出如下

Meowing like a regular cat! meow!
Meowing like a tiger! MREOWWW!
Meowing like an ocelot! mews!

为什么被称为运行时多态?

函数的多态发生于运行时(运行时通过在虚函数表中查找地址来确定函数的resolution)

The resolution of polymorphic function calls happens at runtime through an indirection via the virtual table.

——即,编译的时候,编译器并没有确定需要调用的地址,而是在运行时才会确定。(运行时通过在虚函数表中,找到对应的函数指针)

3.2 参数多态

参数多态主要是,让不同的类型,可以运行相同的代码。

举例:C++中关于max()函数的定义

#include 
#include 

template <class T>
T max(T a, T b) {
 return a > b ? a : b;
}

int main() {
 std::cout << ::max(9, 5) << std::endl;     // 9

 std::string foo("foo"), bar("bar");
 std::cout << ::max(foo, bar) << std::endl; // "foo"
}

这里的max函数就是类型T上的一个多态函数。

编译时多态? —— 因为是编译时确定的(编译时获取到类型,就确定了准确的max函数)

3.3 重载多态

同名函数 / 运算符,对不同类型有不同的效果。这个比较常见,比如函数重载和运算符重载

这里对add函数进行重载,让他分别可以对int类型和std::string类型起作用:

#include 
#include 

int add(int a, int b) {
 return a + b;
}

std::string add(const char *a, const char *b) {
 std::string result(a);
 result += b;
 return result;
}

int main() {
 std::cout << add(5, 9) << std::endl; // 14
 std::cout << add("hello ", "world") << std::endl; // hello world
}

3.4 强制类型多态(类型转换)

经常需要发生比如(float b得到的是一个int类型的输入, int a得到的是一个float类型的输入 —— 需要类型转换)

float b = 6; // int gets promoted (cast) to float implicitly
int a = 9.99 // float gets demoted to int implicitly

使用C++/C的强制类转换的时候也同样会用到,比如(unsigned int *) or (int) or C++'s static_cast, const_cast, reinterpret_cast, or dynamic_cast.

发生在隐式( isn’t explicit)调用类的构造函数时

#include 

class A {
 int foo;
public:
 A(int ffoo) : foo(ffoo) {}
 void giggidy() { std::cout << foo << std::endl; }
};

void moo(A a) {
 a.giggidy();
}

int main() {
 moo(55);     // prints 55
}

上面的代码能够正确输出内容,是因为moo(55)的时候,进行了类型转换int -> A

定义int操作符的例子(定义int操作要return this -> v

class CrazyInt {
 int v;
public:
 CrazyInt(int i) : v(i) {}
 operator int() const { return v; } // conversion from CrazyInt to int
};

调用

#include 

void print_int(int a) {
 std::cout << a << std::endl;
}

int main() {
 CrazyInt b = 55;
 print_int(999);    // prints 999
 print_int(b);      // prints 55
}

这里传入了bclass CrazyInt类型),但是仍然转换成了int类型。

你可能感兴趣的:(c++)