面向对象——封装、继承、多态(多态的四种类型)
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 子类型(运行时)多态

猫科动物类: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 <iostream>
#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, tiger,ocelot都是派生于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 <iostream>
#include <string>
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 <iostream>
#include <string>
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 <iostream>
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 <iostream>
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
}
这里传入了b(class CrazyInt类型),但是仍然转换成了int类型。
912

被折叠的 条评论
为什么被折叠?



