把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
- public: 所有实体都可以访问
- protected: 只允许本类(和子类)的成员函数访问
- private: 只允许本类的成员函数+友元类或友元函数访问(友元类暂时还没学习)
派生类(子类) : 基类(父类)。
举例: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):基本类型转换、自定义类型转换;
猫科动物类: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, 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#include template 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类型起作用:
#include3.4 强制类型多态(类型转换)#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 }
经常需要发生比如(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)调用类的构造函数时
#includeclass 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
};
调用
#includevoid 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类型。



