为了理解多态的进一步应用,老师带着我们简单分析了一下cocos2dx的入口,深刻的体会到了虚函数和类静态成员变量的作用。以此给大家分享一下这个过程。
自实现 MyString 类
自实现 myString 类主要目的是剖析系统内部的 string 类的一些实现方法以及加强对类封装、运算符重载等特性的掌握。其中包含了几项非常重要的功能实现。
- 使用构造器创建对象。
- 拷贝构造器创建对象。
- 赋值运算符重载构造对象。
- []运算符重载构造对象数组。
- ==运算符重载判断对象是否相等。
- +运算符重载实现对象相加。
- >> << 流输入输出运算符实现打印和输入。
具体的实现代码分三个部分,一个 MyString.h 文件,包含类的声明和结构。一个 MyString.cpp 文件,包含类的成员及友元函数实现。最终是一个 main.cpp 来测试我们自己的 MyString 类是否可以正常使用。
STL 常用方法集合
我本想将 STL 中各种容器的实现方法和作用全部写一遍,然后每种容器都发一篇文章,但后来发现这样做的意义不大,在 MSDN 或其他一些帮助文档中,他们比我写的要详细,其实我只需要记住每种容器的常用方法,和在什么场合选择合适的容器。下面这张表是我这里的一些常用方法集合。备用参考。
函数模版与类模版
模版是泛型编程中一种重要的手段,泛型编程意思是让多种数据类型的数据都可以在一个代码段算法中使用。泛型的代表作就是STL。其中模版则是让数据类型参数化,让调用者在使用的时候,多传递一个操作数据的类型便可以调用一份“参数个数相同而类型不同,且函数体相同”的代码段,这个代码段可以让多种数据类型都可以计算出正确的结果。我们首先来看一个常规情况下函数重载的例子。
“虚函数表”推演及多态的原理
C++ 的多态性据前辈们所说,是非常难以理解的一部分内容,虽然他实现很简单,但是套用到各种设计模式后,你会非常难以理解,但无论怎样,笔者始终认为,如果了解了内部的实现原理,实际就不会那么难了。本文将介绍虚函数表的相关内容,阐述了它与多态之间难以割舍的关系。
抽象类纯虚函数与虚析构
纯虚函数,一般是在设计一个基类时使用的,它将接口函数设置为纯虚函数后,只提供子类去继承并实现,以形成多态,除此以外不提供任何其他功能,我们称这种类为抽象类(abstract)。
多态形成的三要素
上一篇文章中,我们看到了简单的赋值兼容模型,将子类赋值给父类对象时,调用共有的同名接口时,调用的依然还是父类的成员函数。在 C++ 中,有一个总要的概念,那就是多态。通过父类提供一些虚函数,让子类继承下去并实现为另外的功能,然后将子类对象的地址赋值给父类的对象指针。这样再次使用父类的指针调用共有同名接口时,你会发现它竟然调用的是子类的方法。这一切都来源于一个关键字“virtual”。
子类赋值父类的赋值兼容
C++ 中,类型的匹配检测是非常严格的,但是你会发现一个现象,如果一个类继承了另外一个类,把子类的对象赋值给父类的时候,系统不但不提示错误,而且程序还能顺利的编译通过并运行。这其实就是 C++ 内部提供的赋值兼容的过程,但是要注意,如果子类数据成员比父类多,则会出现数据截断。具体表现形式如下图:
多继承三角和钻石问题(虚继承)
上一篇文章我们简单介绍了一下多继承的语法,但是我们遇到了一个问题,那就是如果多个父类具有相同名称的成员变量或成员方法,子类在调用的时候就会出现二义性问题,子类不知道选择哪一个父类的变量或方法,我们称之为三角问题。如下所示:
多继承案例及常见问题
多继承,是希望一个子类可以继承多个父类的资源,使自己的功能更加强大,有一个床类、一个沙发类,我们希望将两个类的功能整合到一起,成为一个“沙发床”的类,即可以睡觉、又可以做。这就是多继承的应用。当然我们说的有一些抽象,下面代码演示了多继承的案例。
派生类的实始化、初始化顺序
1、先实始化父类成员,调用父类的构造函数,有多个基类的从左向右按声明顺序实始化。
2、内嵌对象实始化,内嵌对像的构造器。如果父类或是内嵌对象,有无参构造器的话,可以不用显示的调用。如无无参且未调用则会报错。
3、派生类的构造器。
继承权限及关系简单阐述
类具有三种成员属性,一种是public、一种是protected、一种是private。这三种在派生的类中也有不同的访问权限,当然不单单只看父类的成员属性,还要看派生类以什么方式继承父类,如下代码所示:
shadow、overload、override
shadow(阴影)、overload(重载)、override(覆写),这三个概念一直有人非常混淆,很幸运,经过老师的悉心教导,我总结了一下三个概念的不同之处。
shadow:发生在父子之间,需要函数名相同即可构成 shadow (阴影),构成 shadow 后可通过域运算符来访问对应类中的函数。
#include <iostream> using namespace std; class A { public: void display() { cout << "A display" << endl; } }; // B 继承了 A class B : public A { public: void display() { cout << "B display" << endl; } }; int main(int argc, char* argv[]) { B b; b.display(); // 域运算符来访问对应类中的函数 b.A::display(); return 0; }
overload:发生在同一作用域内(同一个类中)。同名,参数不同(类型、个数、顺序)无关返回值就会构成 overload (重载)。
#include <iostream> using namespace std; class A { public: void display() { cout << "A display" << endl; } // 构成重载 void display(int i) { cout << "A display int" << endl; } }; int main(int argc, char* argv[]) { A a; a.display(); a.display(10); return 0; }
override:发生在派生类中,实现了父类的虚函数成为 override(覆写),需要函数名、返回值、参数个数及类型都一模一样,函数体可以不同。虚函数在后面会有详细介绍。
#include <iostream> using namespace std; class A { public: virtual void display() { cout << "A display" << endl; } }; class B : public A { public: // 继承了A类, void display() { cout << "B display" << endl; } }; int main(int argc, char* argv[]) { // 多态小例子 A *a = new B; a->display(); return 0; }
派生类的构造过程
1、先基类、后对象、再子类
多继承,初始化顺序跟基类的声明顺序有关,从左到右。
对象 ,与声明类的顺序有关,从上到下。
智能指针入门 auto_ptr
所谓智能指针,就是可以随便申请而无需管理自动释放,就像 java 或 C# 的垃圾回收机制一样。我们无需关心销毁只管尽情的申请,系统提供了一个 auto_ptr 类可以使用这样的功能。后面我们也会用简单的代码示例介绍 auto_ptr 内部是如何实现的。代码如下: