多态
概述
所谓多态,其实就是:一个接口,多种方法;或者换种说法:发出同样的消息(函数调用),被不同类型的对象接收时(不同的函数拥有者),有可能导致完全不同的行为。
我们前面说过的函数重载(当然也包括运算符重载),就是一种多态,一个函数名(接口),对应多种具体的实现,它属于静态的多态,下一章要讲的模板也是静态多态,一套接口(函数/类)对应不同的数据类型或者行为(因为算法本身也可以作为模板参数);另外基于对象的编程也可以看成是静态多态,接口类里面是一些function,然后具体使用时,可以使用bind绑定不同的实现,类似于挂衣架,流水的衣服,铁打的挂衣架。
静态多态是指编译期就可以确定函数的入口地址,而本章要讲的是动态多态,它只有到运行期才能确定函数入口地址。动态多态在效率上要比静态多态差,因为有额外的运行时开销。
放到C++语言层面,多态其实就是指同一个操作作用于不同的对象会产生不同的响应。
多态的优点:多态使得我们可以以一致的观点对待同一基类的所有派生类,减轻了分别设计的负担,提高了代码重用度。
C++通过虚函数来实现动态多态以及简单的RTTI。
静态联编与动态联编:
静态联编:
程序调用函数时,具体应使用哪个代码块是由编译器决定的。以函数重载为例,C++编译器根据传递给函数的参数和函数名决定具体要使用哪一个函数,称为联编(binding)。
编译器可以在编译过程中完成这种联编,在编译过程中进行的联编叫静态联编(static binding)或早期联编(early binding)。
动态联编:
在一些场合下,编译器无法在编译过程中完成联编,必须在程序运行时完成选择,因此编译器必须提供这么一套称为“动态联编”(dynamic binding)的机制,也叫晚期联编(late binding),C++通过虚函数来实现动态联编。
虚函数
虚函数的定义和声明
将成员函数声明为虚函数,只需要在成员函数原型前加一个关键字virtual即可。
注:
1. 构造函数不能声明为虚函数
2. 虚函数不能声明为static函数
如果一个基类的成员函数定义为虚函数,那么,它在所有派生类中也保持为虚函数;即使在派生类中省略了virtual关键字,也仍然是虚函数。
派生类可根据需要对基类虚函数进行override,上一章说过,override的格式要求很严:
- 与基类的虚函数有相同的参数个数;
- 与基类的虚函数有相同的参数类型;
- 与基类的虚函数有相同的返回类型。
说白了,就是要一模一样,并且是虚函数。
为什么多态需要虚函数:
#include <iostream>
using std::cout;
using std::endl;
class Base
{
public:
virtual // 加与不加大不相同
void disp()
{
cout << "Base::disp" << endl;
}
};
class Child1 : public Base
{
public:
void disp() // 派生类child1中定义的disp函数将base类中定义的disp函数隐藏
{
cout << "Child1::disp" << endl;
}
};
class Child2 : public Base
{
public:
void disp() // 派生类child2中定义的disp函数同样会隐藏base类中定义的disp函数
{
cout << "Child2::disp" << endl;
}
};
void Display(Base* pb) // 目的是实现同一接口(函数),处理不同类对象
{
pb->disp();
}
int main()
{
Base *pBase = NULL;
Base obj_base;
Child1 obj_child1;
Child2 obj_child2;
pBase = &obj_child1;
pBase->disp(); // no virtual: Base::disp | virtual: Child1::disp
// 通过指针调用
Display(&obj_base); // no virtual: Base::disp | virtual: Base::disp
Display(&obj_child1); // no virtual: Base::disp | virtual: Child1::disp
Display(&obj_child2); // no virtual: Base::disp | virtual: Child2::disp
(*pBase).disp(); // no virtual: Base::disp | virtual: Child1::disp
getchar();
return 0;
}
从代码中可以看到,只有虚函数,才能实现一个接口(void Display(Base*)),多种行为(Display(&obj_base), Display(&obj_child1), Display(&obj_child2)具有不同行为)。至于虚函数时怎样实现多态的我们后面会分析。
虚函数的访问
通过对象名访问:
和普通函数一样,虚函数一样可以通过对象名来调用,此时编译器采用的是静态联编。
通过对象名访问虚函数时, 调用哪个类的函数取决于定义对象名的类型。对象类型是基类时,就调用基类的函数;对象类型是子类时,就调用子类的函数。
通过指针访问:
使用指针访问非虚函数时,编译器根据指针本身的类型决定要调用哪个函数,而不是根据指针指向的对象类型;使用指针访问虚函数时,编译器根据指针所指对象的类型决定要调用哪个函数(动态联编),而与指针本身的类型无关。
通过引用访问
使用引用访问虚函数,与使用指针访问虚函数类似;不同的是,引用一经声明后,引用变量本身无论如何改变,其调用的函数就不会再改变,始终指向其开始定义时的函数。
类成员函数中访问
在类内的成员函数中访问该类层次中的虚函数,采用动态联编,要使用this指针。通过构造函数或析构函数中访问:
构造函数和析构函数是特殊的成员函数,在其中访问虚函数时,C++采用静态联编,即在构造函数或析构函数内,即使是使用“this->虚函数名”的形式来调用,编译器仍将其解释为静态联编的“本类名::虚函数名”。即它们所调用的虚函数是自己类中定义的函数,如果在自己的类中没有实现该函数,则调用的是基类中的虚函数。但绝不会调用任何在派生类中重定义的虚函数。
炎炎夏日,来个例子清爽一下:
#include <iostream>
class Base
{
public:
void Show()
{
Disp(); // <=> this->Disp()
}
virtual void Disp()
{
std::cout << "Base::Disp()" << std::endl;
}
};
class Child : public Base
{
public:
//void Show()
//{
// Disp();
//}
void Disp() override
{
std::cout << "Child::Disp()" << std::endl;
}
};
int main(void)
{
Base base;
Child child;
// 直接通过对象调用,与普通继承效果一样
base.Disp(); // Base::Disp()
child.Disp(); // Child::Disp()
child.Child::Disp(); // Child::Disp()
child.Base::Disp(); // Base::Disp()
// 通过基类指针调用,触发多态
Base *p = &child;
p->Disp(); // Child::Disp()
Base *p2 = &base;
p2 = &child;
p2->Disp(); // Child::Disp()
// 通过引用调用,与指针类似,但由于引用的特性,必须定义同时初始化,再不可更改绑定
// 即使改了,也不是改的引用,而是引用的对象,这与指针不同。
Base &ref = child;
ref.Disp(); // Child::Disp()
Base &ref2 = base;
ref2 = child; // 引用还是指向base,相当于base = child;
// 没有多态,存在对象切割,现在base就是child'体内'的那个Base了。
ref2.Disp(); // Base::Disp()
// 通过成员函数调用
p = &child;
p->Show(); // Child::Disp()
p = &base;
p->Show(); // Base::Disp()
return 0;
}
虚析构函数
虽然构造函数不能被定义成虚函数,但析构函数可以定义为虚函数,一般来说,如果类中定义了虚函数,析构函数也应被定义为虚析构函数,尤其是类内有申请的动态内存,需要清理和释放的时候。
只需要在基类的析构函数前面加virtual即可,派生类的析构函数会自动变为虚函数的。
如果一个类要作为多态基类,就应当使用虚析构函数。
如果一个类永远不会被派生类继承,就不要定义成虚函数,无端增加数据复杂性。
#include <iostream>
using std::cout;
using std::endl;
class Base
{
private:
char* data;
public:
Base()
{
data = new char[64];
cout << "Base::Base()" << endl;
};
virtual ~Base() // 虚析构函数
{
delete[] data;
cout << "Base::~Base()" << endl;
};
};
class Child : public Base
{
private:
char* m_data;
public:
Child() :Base()
{
m_data = new char[64];
cout << "Child::Child()" << endl;
};
~Child() // 析构函数,继承虚拟virtual,不需要再加virtual
{
delete[] m_data;
cout << "Child::~Child()" << endl;
};
};
class GrandChild :public Child
{
private:
char* mm_data;
public:
GrandChild() :Child()
{
mm_data = new char[64];
cout << "GrandChild::GrandChild()" << endl;
};
~GrandChild() // 虚析构函数,virtual从继承结构中得来
{
delete[] mm_data;
cout << "GrandChild::~GrandChild()" << endl;
};
};
int main()
{
Base *pB = new Child;
delete pB; // 调用的是指针指向的实际对象(Child对象)的析构函数,而
// Child::Child()又会调用Base::Base(),这样就没有内存泄漏。
cout << "=============" << endl;
Child* pC = new GrandChild;
delete pC; // 调用的是指针指向的实际对象(GrandChild对象)的析构函数,而
// GrandChild::GrandChild()又会调用Child::Child(),这样
// 就没有内存泄漏。
cout << "======不推荐下面这种用法=======" << endl;
GrandChild *pG = (GrandChild *)new Base;
delete pG; // 调用的是指针指向的实际对象(Base对象)的析构函数
// -> 如果去掉基类析构函数前的virtual, 执行到
// delete[] mm_data时调用的将是GrandChild::~GrandChild(),
// 会报内存错误, 因为mm_data, m_data根本就没有new
return 0;
}
虚函数表
如果类中包含有虚成员函数,在用该类实例化对象时,对象的第一个成员(4/8个字节)将是一个指向虚函数表(vftable)的指针(vfptr)。虚函数表记录运行过程中实际应该调用的虚函数的入口地址。
更详细的可以参见陈皓的C++ 对象的内存布局。
单继承 + 虚函数:
#include <iostream>
#include <fstream>
using std::endl;
std::ofstream cout("C:/Users/StarrySky/Desktop/text.txt");
class Garbo
{
public:
~Garbo()
{
cout.close();
}
};
Garbo garbo;
class Parent {
public:
Parent () : iparent (10) {}
virtual void f() { cout << "Parent::f()"; }
virtual void g() { cout << "Parent::g()"; }
virtual void h() { cout << "Parent::h()"; }
public:
int iparent;
};
class Child : public Parent {
public:
Child() : ichild(100) {}
virtual void f() { cout << "Child::f()"; }
virtual void g_child() { cout << "Child::g_child()"; }
virtual void h_child() { cout << "Child::h_child()"; }
public:
int ichild;
};
class GrandChild : public Child{
public:
GrandChild() : igrandchild(1000) {}
virtual void f() { cout << "GrandChild::f()"; }
virtual void g_child() { cout << "GrandChild::g_child()"; }
virtual void h_grandchild() { cout << "GrandChild::h_grandchild()"; }
public:
int igrandchild;
};
int main(void)
{
typedef void(*Fun)(void);
GrandChild gc;
Fun pFun;
int** pVtab = (int**)&gc;
cout << "[0](" << pVtab << ") GrandChild::vfptr" << endl;
for (int i=0; (Fun)pVtab[0][i]!=NULL; i++){
pFun = (Fun)pVtab[0][i];
cout << " |-- ["<<i<<"](" << pVtab[0] + i << ") ";
pFun();
cout << endl;
}
cout << "[1](" << pVtab+1 << ") Parent::iparent = " << (int)pVtab[1] << endl;
cout << "[2](" << pVtab+2 << ") Child::ichild = " << (int)pVtab[2] << endl;
cout << "[3](" << pVtab+3 << ") GrandChild::igrandchild = " << (int)pVtab[3] << endl;
return 0;
}
// [0](0018F82C) GrandChild::vfptr
// |-- [0](001899B8) GrandChild::f()
// |-- [1](001899BC) Parent::g()
// |-- [2](001899C0) Parent::h()
// |-- [3](001899C4) GrandChild::g_child()
// |-- [4](001899C8) Child::h_child()
// |-- [5](001899CC) GrandChild::h_grandchild()
// [1](0018F830) Parent::iparent = 10
// [2](0018F834) Child::ichild = 100
// [3](0018F838) GrandChild::igrandchild = 1000
派生类override的虚函数,会覆盖虚函数表中基类的虚函数入口,多级继承,依次类推就是了。
多继承+虚函数:
#include <iostream>
#include <fstream>
using std::endl;
std::ofstream cout("C:/Users/StarrySky/Desktop/text.txt");
class Garbo {
public:
~Garbo()
{
cout.close();
}
};
Garbo garbo;
class Base1 {
public:
Base1 () : iBase1(10) {}
virtual void f() { cout << "Base1::f()"; }
virtual void g() { cout << "Base1::g()"; }
virtual void h() { cout << "Base1::h()"; }
public:
int iBase1;
};
class Base2 {
public:
Base2() : iBase2(200) {}
virtual void f() { cout << "Base2::f()"; }
virtual void g() { cout << "Base2::g()"; }
virtual void h() { cout << "Base2::h()"; }
public:
int iBase2;
};
class Base3 {
public:
Base3() : iBase3(300) {}
virtual void f() { cout << "Base3::f()"; }
virtual void g() { cout << "Base3::g()"; }
virtual void h() { cout << "Base3::h()"; }
public:
int iBase3;
};
class Derived : public Base1, public Base2, public Base3 {
public:
Derived() : iDerived(1000) {}
virtual void f() { cout << "Derived::f()"; }
virtual void g_derived() { cout << "Derived::g_derived()"; }
public:
int iDerived;
};
int main(void)
{
typedef void(*Fun)(void);
Fun pFun;
Derived d;
int** pVtab = (int**)&d;
cout << "[0](" << pVtab << ") Base1::vfptr" << endl;
pFun = (Fun)pVtab[0][0];
cout << " |-- [0] "; pFun(); cout << endl;
pFun = (Fun)pVtab[0][1];
cout << " |-- [1] "; pFun(); cout << endl;
pFun = (Fun)pVtab[0][2];
cout << " |-- [2] "; pFun(); cout << endl;
pFun = (Fun)pVtab[0][3];
cout << " |-- [3] "; pFun(); cout << endl;
pFun = (Fun)pVtab[0][4];
cout << " |-- [4] "; cout << pFun << endl;
cout << "[1] Base1.ibase1 = " << (int)pVtab[1] << endl;
int s = sizeof(Base1)/4;
cout << "[" << s << "](" << pVtab + s << ") Base2::vfptr" << endl;
pFun = (Fun)pVtab[s][0];
cout << " |-- [0] "; pFun(); cout << endl;
pFun = (Fun)pVtab[s][1];
cout << " |-- [1] "; pFun(); cout << endl;
pFun = (Fun)pVtab[s][2];
cout << " |-- [2] "; pFun(); cout << endl;
pFun = (Fun)pVtab[s][3];
cout << " |-- [3] " << pFun << endl;
cout << "["<< s+1 <<"] Base2.ibase2 = " << (int)pVtab[s+1] << endl;
s = s + sizeof(Base2)/4;
cout << "[" << s << "](" << pVtab + s << ") Base3::vfptr" << endl;
pFun = (Fun)pVtab[s][0];
cout << " |-- [0] "; pFun(); cout << endl;
pFun = (Fun)pVtab[s][1];
cout << " |-- [1] "; pFun(); cout << endl;
pFun = (Fun)pVtab[s][2];
cout << " |-- [2] "; pFun(); cout << endl;
pFun = (Fun)pVtab[s][3];
cout << " |-- [3] " << pFun << endl;
s++;
cout << "["<< s <<"](" << pVtab + s << ") Base3.ibase3 = " << (int)pVtab[s] << endl;
s++;
cout << "["<< s <<"](" << pVtab + s << ") Derive.iderive = " << (int)pVtab[s] << endl;
return 0;
}
// [0](0043FC68) Base1::vfptr
// |-- [0] Derived::f()
// |-- [1] Base1::g()
// |-- [2] Base1::h()
// |-- [3] Derived::g_derived()
// |-- [4] 00000000
// [1] Base1.ibase1 = 10
// [2](0043FC70) Base2::vfptr
// |-- [0] Derived::f()
// |-- [1] Base2::g()
// |-- [2] Base2::h()
// |-- [3] 00000000
// [3] Base2.ibase2 = 200
// [4](0043FC78) Base3::vfptr
// |-- [0] Derived::f()
// |-- [1] Base3::g()
// |-- [2] Base3::h()
// |-- [3] 00000000
// [5](0043FC7C) Base3.ibase3 = 300
// [6](0043FC80) Derive.iderive = 1000
- 每个父类都有自己的虚表。
- 子类新增的的虚函数被添加到了第一个父类的虚表后面。
- 内存布局中,其父类布局依次按声明顺序排列。
- 每个父类的虚表中的f()函数都被override成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
钻石型继承-单级 + 虚函数:
#include <iostream>
#include <fstream>
using std::endl;
std::ofstream cout("C:/Users/StarrySky/Desktop/text.txt");
class Garbo {
public:
~Garbo()
{
cout.close();
}
};
Garbo garbo;
class B
{
public:
B() : ib(0), cb('B') {}
virtual void f() { cout << "B::f()" << endl; }
virtual void Bf() { cout << "B::Bf()" << endl; }
public:
int ib;
char cb;
};
class B1 : virtual public B
{
public:
B1() : ib1(11), cb1('1') {}
virtual void f() { cout << "B1::f()" << endl; }
virtual void f1() { cout << "B1::f1()" << endl; }
virtual void Bf1() { cout << "B1::Bf1()" << endl; }
public:
int ib1;
char cb1;
};
class B2 : virtual public B
{
public:
B2() : ib2(12), cb2('2') {}
virtual void f() { cout << "B2::f()" << endl; }
virtual void f2() { cout << "B2::f2()" << endl; }
virtual void Bf2() { cout << "B2::Bf2()" << endl; }
public:
int ib2;
char cb2;
};
class D : public B1, public B2
{
public:
D() : id(100), cd('D') {}
virtual void f() { cout << "D::f()" << endl; }
virtual void f1() { cout << "D::f1()" << endl; }
virtual void f2() { cout << "D::f2()" << endl; }
virtual void Df() { cout << "D::Df()" << endl; }
public:
int id;
char cd;
};
int main(void)
{
typedef void(*Fun)(void);
Fun pFun = NULL;
B1 bb1;
int** pVtab = (int**)&bb1;
cout << "[0] B1::_vfptr->" << endl;
pFun = (Fun)pVtab[0][0];
cout << " [0] "; pFun(); //B1::f1();
cout << " [1] ";
pFun = (Fun)pVtab[0][1]; pFun(); //B1::bf1();
cout << " [2] " << pVtab[0][2] << endl;
cout << "[1] B1::_vptr->" << endl;
cout << " [0] " << pVtab[1][0] << endl;
cout << " [1] " << pVtab[1][1] << endl;
cout << "[2] B1::ib1 = ";
cout << (int)*((int*)(&bb1) + 2) << endl; // B1::ib1
cout << "[3] B1::cb1 = ";
cout << (char)*((int*)(&bb1) + 3) << endl; // B1::cb1
cout << "[3] 0x ";
cout << (char)*((int*)(&bb1) + 4) << endl; // B1::cb1
cout << "[5] B::_vfptr->" << endl;
pFun = (Fun)pVtab[5][0];
cout << " [0] "; pFun(); // B1::f();
pFun = (Fun)pVtab[5][1];
cout << " [1] "; pFun(); // B::Bf();
cout << " [2] " << "0x" << (Fun)pVtab[5][2] << endl;
cout << "[6] B::ib = ";
cout << (int)*((int*)(&bb1) + 6) << endl; // B::ib
cout << "[7] B::cb = ";
return 0;
}
// [0] B1::_vfptr->
// [0] B1::f1()
// [1] B1::Bf1()
// [2] 0
// [1] B1::_vptr->
// [0] -4
// [1] 16
// [2] B1::ib1 = 11
// [3] B1::cb1 = 1
// [3] 0x 0
// [5] B1::_vfptr->
// [0] B1::f()
// [1] B::Bf()
// [2] 0
// [6] B::ib = 0
// [7] B::cb = B
钻石型继承 + 虚函数:
#include <iostream>
#include <fstream>
using std::endl;
std::ofstream cout("C:/Users/StarrySky/Desktop/text.txt");
class Garbo {
public:
~Garbo()
{
cout.close();
}
};
Garbo garbo;
class B
{
public:
B() : ib(0), cb('B') {}
virtual void f() { cout << "B::f()" << endl; }
virtual void Bf() { cout << "B::Bf()" << endl; }
public:
int ib;
char cb;
};
class B1 : virtual public B
{
public:
B1() : ib1(11), cb1('1') {}
virtual void f() { cout << "B1::f()" << endl; }
virtual void f1() { cout << "B1::f1()" << endl; }
virtual void Bf1() { cout << "B1::Bf1()" << endl; }
public:
int ib1;
char cb1;
};
class B2 : virtual public B
{
public:
B2() : ib2(12), cb2('2') {}
virtual void f() { cout << "B2::f()" << endl; }
virtual void f2() { cout << "B2::f2()" << endl; }
virtual void Bf2() { cout << "B2::Bf2()" << endl; }
public:
int ib2;
char cb2;
};
class D : public B1, public B2
{
public:
D() : id(100), cd('D') {}
virtual void f() { cout << "D::f()" << endl; }
virtual void f1() { cout << "D::f1()" << endl; }
virtual void f2() { cout << "D::f2()" << endl; }
virtual void Df() { cout << "D::Df()" << endl; }
public:
int id;
char cd;
};
int main(void)
{
typedef void(*Fun)(void);
Fun pFun = NULL;
B1 bb1;
int** pVtab = (int**)&bb1;
cout << "[0] D::B1::_vptr->" << endl;
pFun = (Fun)pVtab[0][0];
cout << " [0] "; pFun(); //D::f1();
pFun = (Fun)pVtab[0][1];
cout << " [1] "; pFun(); //B1::Bf1();
pFun = (Fun)pVtab[0][2];
cout << " [2] "; pFun(); //D::Df();
pFun = (Fun)pVtab[0][3];
cout << " [3] ";
cout << pFun << endl;
cout << "[1] B1::_vptr->" << endl;
cout << " [0] " << pVtab[1][0] << endl;
cout << " [1] " << pVtab[1][1] << endl;
cout << "[2] B1::ib1 = ";
cout << *((int*)(&dd) + 2) << endl; //B1::ib1
cout << "[3] B1::cb1 = ";
cout << (char)*((int*)(&dd) + 3) << endl; //B1::cb1
//---------------------
cout << "[4] D::B2::_vptr->" << endl;
pFun = (Fun)pVtab[4][0];
cout << " [0] "; pFun(); //D::f2();
pFun = (Fun)pVtab[4][1];
cout << " [1] "; pFun(); //B2::Bf2();
pFun = (Fun)pVtab[4][2];
cout << " [2] ";
cout << pFun << endl;
cout << "[5] B1::_vptr->" << endl;
cout << " [0] " << pVtab[5][0] << endl;
cout << " [1] " << pVtab[5][1] << endl;
cout << "[6] B2::ib2 = ";
cout << (int)*((int*)(&dd) + 6) << endl; //B2::ib2
cout << "[7] B2::cb2 = ";
cout << (char)*((int*)(&dd) + 7) << endl; //B2::cb2
cout << "[8] D::id = ";
cout << *((int*)(&dd) + 8) << endl; //D::id
cout << "[9] D::cd = ";
cout << (char)*((int*)(&dd) + 9) << endl;//D::cd
cout << "[10] = 0x";
cout << (int*)*((int*)(&dd) + 10) << endl;
//---------------------
cout << "[11] D::B::_vptr->" << endl;
pFun = (Fun)pVtab[11][0];
cout << " [0] "; pFun(); //D::f();
pFun = (Fun)pVtab[11][1];
cout << " [1] "; pFun(); //B::Bf();
pFun = (Fun)pVtab[11][2];
cout << " [2] ";
cout << pFun << endl;
cout << "[12] B::ib = ";
cout << *((int*)(&dd) + 12) << endl; //B::ib
cout << "[13] B::cb = ";
cout << (char)*((int*)(&dd) + 13) << endl;//B::cb
return 0;
}
// [0] B1::_vfptr->
// [0] D::f1()
// [1] B1::Bf1()
// [2] D::Df()
// [3] 0
// [1] B1::_vptr->
// [0] -4
// [1] 44
// [2] B1::ib1 = 11
// [3] B1::cb1 = 1
// [4] B2::_vfptr->
// [0] D::f2()
// [1] B2::Bf2()
// [2] 0
// [5] B2::_vptr->
// [0] -4
// [1] 28
// [6] B1::ib1 = 12
// [7] B1::cb1 = 2
// [8] D::id = 100
// [9] D::cd = D
// [10] 0x0
// [11] B::_vfptr->
// [0] D::f()
// [1] B::Bf()
// [2] 0
// [12] B::ib = 0
// [13] B::cd = B
- 先是B1,然后是B2,接着是D,而B这个超类的实例都放在最后的位置。
- 有一个NULL分隔符把B和B1和B2的布局分开。。
纯虚函数与抽象类
当在基类中无法为虚函数提供任何有实际意义的定义时,可以将该虚函数声明为纯虚函数,它的实现留给该基类的派生类去做。
class 类名
{
//...
virtual 类型 函数名 (参数表)=0; // 纯虚函数
//...
};
一个类可以包含多个纯虚函数。只要类中含有一个纯虚函数,该类便为抽象类。一个抽象类只能作为基类来派生新类,不能创建抽象类的对象。
和普通的虚函数不同,在派生类中一般要对基类中纯虚函数进行重定义,或者在派生类中再次将该虚函数声明为纯虚函数。这说明,抽象类的派生类也可以是抽象类,只有在派生类中给出了基类中所有纯虚函数的实现时,该派生类便不再是抽象类。抽象类只起到提供统一接口的作用。
对一个类来说,如果只定义了protected型(不能是private的)的构造函数而没有提供public构造函数,无论是在外部还是在派生类中作为其对象成员都不能创建该类的对象,但可以由其派生出新的类,这种能派生新类,却不能创建自己对象的类是另一种形式的抽象类
工厂类-动态创建对象
Dynbase.h:
#ifndef DYNBASE_H_
#define DYNBASE_H_
#include <string>
#include <iostream>
using std::cout;
using std::endl;
using std::string;
#include <map>
using std::map;
typedef void* (*CREATE_FUNC)();
// 工厂类->动态创建对象
// 新增类型后工厂类代码不需要做任何改动,比上一节用if-else好。
// 都是静态的,进入main之前就初始化好了。
class DynObjectFactory
{
public:
static void* CreateObject(const string& name)
{
map<string,CREATE_FUNC>::const_iterator it;
it = map_cls_.find(name);
if (it == map_cls_.end()) { // 没找到
return NULL;
} else {
return it->second();
}
}
static void Register(const string& name, CREATE_FUNC func)
{
map_cls_[name] = func;
}
private:
static map<string, CREATE_FUNC> map_cls_;
};
// gcc __attribute((weak))
__declspec(selectany) map<string, CREATE_FUNC> DynObjectFactory::map_cls_; // 这里不需要再加static
//
//#define REGISTER_CLASS(class_name) \
//static void* class_name##NewInstance() { \
// return new class_name;\
//}\
//DynObjectFactory::Register(#class_name, class_name##NewInstance)
// 全局作用于不能调用除了main之外的函数,但是利用静态对象先于main构造+构造函数,就能实现全局调
// 用函数[静态对象的构造函数].
//class Register
//{
//public:
// Register(const string& name, CREATE_FUNC func)
// {
// cout << "Register ..." << endl;
// DynObjectFactory::Register(name, func);
// }
//};
//#define REGISTER_CLASS(class_name) \
//class class_name##Register \
//{ \
//public: \
// static void* NewInstance() \
// { \
// return new class_name; \
// } \
//private: \
// static Register reg_; \
//}; \
//Register class_name##Register::reg_(#class_name, class_name##Register::NewInstance)
template<typename T>
class DelegatingClass
{
public:
DelegatingClass(const string name)
{
DynObjectFactory::Register(name, NewInstance);
}
static void* NewInstance()
{
return new T;
}
};
#define REGISTER_CLASS(class_name) \
static DelegatingClass<class_name> class_name##Register(#class_name)
#endif // DYNBASE_H_
Shape.cpp:
#include "Shape.h"
#include "DynBase.h"
//#include <iostream>
using std::cout;
using std::endl;
Shape::~Shape()
{
cout << "~Shape ..." << endl;
}
void Circle::Draw()
{
cout << "Circle::Draw() ..." << endl;
}
Circle::~Circle()
{
cout << "~Circle ..." << endl;
}
void Square::Draw()
{
cout << "Square::Draw() ..." << endl;
}
Square::~Square()
{
cout << "~Square ..." << endl;
}
void Rectangle::Draw()
{
cout << "Rectangle::Draw() ..." << endl;
}
Rectangle::~Rectangle()
{
cout << "~Rectangle ..." << endl;
}
REGISTER_CLASS(Circle);
REGISTER_CLASS(Square);
REGISTER_CLASS(Rectangle);
用法:
// 可以把这些东西放到配置文件中去,新增了类这里的代码也不需要改动。
vector<Shape*> v;
Shape* ps;
ps = static_cast<Shape*>(DynObjectFactory::CreateObject("Circle"));
v.push_back(ps);
ps = static_cast<Shape*>(DynObjectFactory::CreateObject("Square"));
v.push_back(ps);
ps = static_cast<Shape*>(DynObjectFactory::CreateObject("Rectangle"));
v.push_back(ps);
RTTI
#include <iostream>
#include <typeinfo>
using std::cout;
using std::endl;
class Shape {
public:
virtual void Draw() = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
public:
void Draw() {
cout << "Circle ..." << endl;
}
};
class Square : public Shape {
public:
void Draw() {
cout << "Square ..." << endl;
}
};
int main(void)
{
Shape *p;
Circle c;
p = &c;
p->Draw();
cout << "================= dynamic_cast ========================" << endl;
// 类型安全的向下转型,只有基类指针确实指向该类时,向该类转型才会成功。
// 这种运行时类型识别,比用虚函数的开销大。基类需要有虚函数才能利用dynamic_cast
// C++的多态机制中,保存了RTTI信息,位于虚表之前,C++的dynamic_cast通过查询该
// 内存段的信息,可以获知运行时信息。
if (dynamic_cast<Circle*>(p)) {
cout << "p is Circle object" << endl;
} else if (dynamic_cast<Square*>(p)) {
cout << "p is Square object" << endl;
} else {
cout << "p is Other object" << endl;
}
cout << "================= typeid ========================" << endl;
// 返回一个type_info类对象
// 效率也没直接虚函数效率高
cout << typeid(*p).name() << endl;
cout << typeid(Circle).name() << endl;
if (typeid(*p).name() == typeid(Circle).name()) {
cout << "p is point to a Circle object" << endl;
((Circle*)p)->Draw();
} else if (typeid(*p).name() == typeid(Square).name()) {
cout << "p is point to a Square object" << endl;
((Square *) p)->Draw();
}
getchar();
return 0;
}
本文介绍了C++中的多态性,主要关注动态多态的实现,即通过虚函数来实现。讲解了虚函数的概念、声明与定义,以及为什么需要虚函数来实现多态。此外,还探讨了虚函数的访问方式,包括通过对象、指针和引用调用。同时,文章提到了虚析构函数的重要性,以及虚函数表在对象内存布局中的角色。最后,讨论了抽象类和纯虚函数的应用。
7943

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



