小记:静默如初,安之若素
多态
- 虚函数覆盖(函数重写),多态概念
如果将基类中的某个成员函数声明为虚函数,那么其子类中与该函数具有相同原型的成员函数也是虚函数,并且对基类中的版本形成覆盖。
这时,通过指向子类对象的基类指针,或者通过引用子类对象的基类引用去调用虚函数,实际被执行的将是子类中的覆盖版本,而不再是基类中的原始版本,这种语法现象被称为多态。
class A
{
public:
virtual void func(void){}//虚函数
};
class B:public A
{
public:
void func(void){} //自动变成虚函数
};
int main(void)
{
B b;
A *pa = &b;//pa指向子类对象的基类指针
A &ra = b;//ra引用子类对象的基类引用
pa->func();//子类中的func
ra.func();//子类中的func
}
1 #include <iostream>
2 using namespace std;
3
4 class Shape
5 {
6 public:
7 Shape(int x, int y):m_x(x),m_y(y){}
8
9 virtual void draw(void)//虚函数
10 {
11 cout << "Drawing : " << m_x << "," << m_y << endl;
12 }
13
14 protected:
15 int m_x;
16 int m_y;
17 };
18
19 class Rect:public Shape
20 {
21 public:
22 Rect(int x, int y, int w, int h):Shape(x, y), m_w(w), m_h(h){}
23
24 void draw(void)
25 {
26 cout << "DrawRect : " << m_x << "," << m_y << "," << m_w << "," << m_h << endl;
27 }
28
29 private:
30 int m_w;
31 int m_h;
32 };
33
34 class Circle:public Shape
35 {
36 public:
37 Circle(int x, int y, int r):Shape(x, y), m_r(r){};
38 void draw(void)
39 {
40 cout << "DrawCircle : " << m_x << "," << m_y << "," << m_r << endl;
41 }
42 private:
43 int m_r;
44 };
45
46 void render(Shape *buffer[])
47 {
48 for(int i = 0; buffer[i] != NULL; i++)
49 {
50 /*调用虚函数时,不再由指针本身类型决定调用哪个版本,
51 而是由实际的目标对象类型决定*/
52 buffer[i] -> draw();
53 }
54 }
55
56 int main(int argc, char *argv[])
57 {
58 Shape *buffer[1024] = {NULL};
59 buffer[0] = new Rect(1,2,3,4);
60 buffer[1] = new Circle(5, 6, 7);
61 buffer[2] = new Rect(5, 6, 7, 8);
62 buffer[3] = new Circle(3, 3, 5);
63 //....
64 render(buffer);
65 return 0;
66 }
2. 虚函数覆盖的条件
1)只有类中的成员函数才能声明为虚函数,但是全局函数,静态成员函数,构造函数都不能声明为虚函数注:析构函数能声明为虚函数
2)只有在基类中以virtual
关键字修饰的成员函数才能作为虚函数被子类覆盖,而与子类中的virtual关键字无关。
3)虚函数在子类中的版本必须和该函数在基类中的版本拥有相同的函数签名,即函数名,形参表和常属性一致
。
4)如果基类中的虚函数返回基本类型的数据,那么该函数在子类中的覆盖版本必须返回相同类型的数据。
5)如果基类中的虚函数返回类类型的指针A*
或引用A&
,那么允许子类的覆盖版本返回子类类型的指针B*
或引用B&
。
class A{};
class B:public A{};
1 #include <iostream>
2 using namespace std;
3
4 class A{};
5 class B:public A{};
6
7 class Base
8 {
9 public:
10 virtual void foo(void)
11 {
12 cout << "Base::foo" << endl;
13 }
14
15 virtual void bar(int d = 0)
16 {
17 cout << "Base::bar" << endl;
18 }
19
20 virtual A* hum(void)
21 {
22 cout << "Base::hum" << endl;
23 }
24 };
25
26 class Derived: public Base
27 {
28 private:
29 /*virtual*/ void foo(void)
30 {
31 cout << "Derived::foo" << endl;
32 }
33
34 void bar(int i = 0) /*const*/
35 {
36 cout << "Derived::bar" << endl;
37 }
38
39 //向上造型
40 B* hum(void)
41 {
42 cout << "Derived::hum" << endl;
43 }
44 };
45
46 int main(void)
47 {
48 Derived d;
49 Base *pb = &d;
50 pb->foo();
51 pb->bar();
52 pb->hum();
53 return 0;
54 }
3. 多态的条件
1)多态特性除了需要满足虚函数的覆盖,还必须通过指针或引用去调用才能表现出来。
2)调用虚函数的指针也可能是this指针,当通过一个子类对象调用基类中成员函数时,该函数里面的this指针
就是一个指向子类对象的基类指针,再通过它去调用虚函数,同样可以表现多态的语法特性。
1 #include <iostream>
2 using namespace std;
3
4 class Base
5 {
6 public:
7 virtual int cal(int x, int y)
8 {
9 return x + y;
10 }
11
12 //void func(Base *this = &d)
13 void func(void)
14 {
15 //cout << this->cal(200, 300) << endl;
16 cout << cal(200, 300) << endl;
17 }
18 };
19
20 class Derived:public Base
21 {
22 public:
23 int cal(int x, int y)
24 {
25 return x * y;
26 }
27 };
28
29 int main(void)
30 {
31 Derived d;
32 //无多态语法
33 //Base b = d;
34 //cout << b.cal(100, 200) << endl;
35 d.func();
36
37 return 0;
38 }
4. 纯虚函数,抽象类,纯抽象类
1)纯虚函数
virtual 返回类型 函数名(形参表)[const]= 0;
2)抽象类
如果一个类中包含了纯虚函数,那么这个类就是抽象类。
注:不能使用抽象类创建对象。
3)纯抽象类(有名接口类)
如果一个类中成员函数都是纯虚函数,那么该类就是纯抽象类。
示例:工厂模式方法
1 //工厂方法模式
2 #include <iostream>
3 using namespace std;
4
5 class PDFParser
6 {
7 public:
8 void parse(const char *pdffile)
9 {
10 cout << "Parse(解析) Rect" << endl;
11 onRect();
12 cout << "Parse(解析) Cicle"<< endl;
13 onCircle();
14 cout << "Parse Text" << endl;
15 onText();
16 cout << "Paese Image" << endl;
17 onImage();
18 }
19
20 private:
21 virtual void onRect(void) = 0;
22 virtual void onCircle(void) = 0;
23 virtual void onText(void) = 0;
24 virtual void onImage(void) = 0;
25
26 };
27
28 class PDFRender:public PDFParser
29 {
30 private:
31 void onRect(void)
32 {
33 cout << "DrawRect" << endl;
34 }
35 void onCircle(void)
36 {
37 cout << "DrawCircle" << endl;
38 }
39 void onText(void)
40 {
41 cout << "ShowText" << endl;
42 }
43 void onImage(void)
44 {
45 cout << "ShowImage" << endl;
46 }
47 };
48
49 int main(void)
50 {
51 PDFRender render;
52 render.parse("xx.pdf");
53 return 0;
54 }
5. 多态实现原理
通过虚函数表和动态绑定来实现:
1)虚函数的动态绑定会增加时间的开销;
2)虚函数表的生成会增加内存的开销。
注:如果没有多态的语法要求,最好不要适用虚函数。
6. 虚析构函数
1)基类的析构函数不会自动调用子类的析构函数。如果对一个指针子类对象的激烈指针适用delete操作符,实际被执行的仅是基类的析构函数,子类的析构函数执行不到,有内存泄露的风险。
class A{...};
class B:public A{...}
A* pa = new B;//pa:指向子类对象的基类指针
delete pa;//内存泄露
2)虚析构来解决上述内存泄露问题。
可以将基类的析构函数声明为虚函数,那么子类的析构函数就也是一个虚函数,并且对基类的虚函数构成有效的覆盖,可以表现多态的语法特性。
这是delete
一个指向子类对象的基类指针,实际被调用的将是子类的析构函数,子类的析构函数在执行结束以后又会自动的调用基类的析构函数,避免了内存泄露。
1 #include<iostream>
2 using namespace std;
3
4 class Base
5 {
6 public:
7 Base(void)
8 {
9 cout << "Base::Base()" << endl;
10 }
11 virtual ~Base(void)
12 {
13 cout << "~Base::Base()" << endl;
14 }
15 };
16
17 class Derived:public Base
18 {
19 public:
20 Derived(void)
21 {
22 cout << "Dervived::Derived()" << endl;
23 }
24 ~Derived(void)
25 {
26 cout << "~Derived::Derived()" << endl;
27 }
28 };
29
30 int main(void)
31 {
32 Base *pb = new Derived;
33 //...
34 //1)pb->析构函数
35 //2)释放内存
36 delete pb;
37
38 return 0;
39 }
~