这些知识点很多前辈都总结的非常好了,我汇总起来便于查阅。
参考了https://lyy-0217.blog.csdn.net/article/details/79428643很多内容,表示感谢
1.认识多态
多态是指同样的消息被不同类型的对象接受时导致不同的行为,所谓的消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数。在程序设计中多态性使用最简单的例子就是运算符,比如“+”,就可以实现整数之间,浮点数之间,双精度浮点数之间以及它们相互的加法运算。同样的消息–相加,被不同类型的对象-变量接受后,不同类型的变量采用不同的方式进行加法运算。如果是不同类型的变量相加,例如浮点数和整数,则先要将浮点数转化成整数,然后再进行加法运算。
面向对象的多态性可以分为4类,重载多态、强制多态、包含多态和参数多态。
重载多态–普通函数及类的成员函数的重载,运算符重载等。
强制多态–比如浮点数和整数相加,首先进行类型强制转化,把整数转化成浮点数再相加。
包含多态–定义于不同类中的同名函数的多态行为,主要通过虚函数来实现。
参数多态–和类模板相关联。
2.类型兼容原则
在深入了解多态前,先认识下类型兼容原则。
类型兼容原则是指在需要基类对象的地方,都可以用公有派生类来替代。在替代之后,派生类对象就可以作为基类的对象来使用,但只能使用从基类继承得成员。
类型兼容规则的引入,对于基类及其公有派生类的对象,我们可以使用相同的函数同一进行处理(因为当函数的形参为基类的对象时,实参可以是派生类的对象),而没有必要为每一个类设计单独的模块,大大提高程序的效率。
兼容包括以下情况:
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
下面是一个例子:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
int mi;
void add(int i)
{
mi += i;
}
void add(int a, int b)
{
mi += (a + b);
}
};
class Child : public Parent
{
public:
int mv;
void add(int x, int y, int z)
{
mv += (x + y + z);
}
};
int main()
{
Parent p; //父类
Child c; //子类
c.mv=0; //不初始化下面p=c会出错
c.mi=0;
p = c; //子类对父类赋值
Parent p1(c); //用子类来初始化父类
Parent& rp = c; //父类引用直接引用子类
Parent* pp = &c; //父类指针直接指向子类
rp.mi = 100;
rp.add(5); // 只能使用从父类继承的成员
rp.add(10, 10); //只能使用从父类继承的成员
/* 为什么编译不过? */
// pp->mv = 1000; // pp是父类指针,指向子类c,那么c对象就退化为父类对象,而mv是子类对象,不能使用
// pp->add(1, 10, 100); // pp只能访问父类对象,并且可以访问父类与子类同名的对象
// 而不用指定作用域。
return 0;
}
当使用父类指针(引用)指向子类对象时:
- 子类对象退化为父类对象
- 只能访问父类中定义的成员
- 可以直接访问被子类覆盖的同名成员而不用指定作用域
3.函数的重写
(1)子类中可以重定义父类中已经存在的成员函数。
(2)这种重定义发生在继承中,叫做函数重写。
(3)函数重写是函数同名覆盖(隐藏)的特殊情况。
那么当函数重写遇上赋值兼容会发生什么?
先来看一个简单的例子:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
void print()
{
cout << "I'm Parent." << endl;
}
};
class Child : public Parent
{
public:
void print()
{
cout << "I'm Child." << endl;
}
};
void how_to_print(Parent* p)
{
p->print();
}
int main()
{
Parent p;
Child c;
how_to_print(&p); // Expected to print: I'm Parent.
how_to_print(&c); // Expected to print: I'm Child.
return 0;
}
运行结果为:
I’m Parent.
I’m Parent.
很显然,这个结果并不是我们想要的,我们想要的是执行 how_to_print(&c); 后打印I’m Child.
如果要解决上面的问题,一种方法是 可以通过作用域分辨符(::)来访问父类中的函数
Child c;
Parent* p=&c;
c.Parent::print();
c.print();
另一种方法是使用多态,使我们可以根据实际的对象类型调用具体的成员重写函数。
当父类指针(引用)指向
(1)父类对象,则调用父类中定义的函数
(2)子类对象,则调用子类中定义的重写函数
C++语言直接支持多态的概念,可以通过使用virtual关键字对多态进行支持,被virtual声明的函数被重写后具有多态性,被virtual声明的函数叫做虚函数.
修改上一个程序:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
virtual void print()
{
cout << "I'm Parent." << endl;
}
};
class Child : public Parent
{
public:
void print()
{
cout << "I'm Child." << endl;
}
};
void how_to_print(Parent* p)
{
p->print();
}
int main()
{
Parent p;
Child c;
how_to_print(&p);
how_to_print(&c);
return 0;
}
运行结果:
I’m Parent.
I’m Child.
父类包含了虚函数,派生类中的重写函数即使没有virtual关键字修饰,派生类的派生类的重写函数也是虚函数.
例子:
#include "stdafx.h"
#include<iostream>
#include<stdio.h>
class father
{
public:
virtual void print()
{
printf("I'm father");
}
};
class son:public father
{
public:
void print()
{
printf("I'm son");
}
};
class grandson:public son
{
public:
void print()
{
printf("I'm grandson");
}
};
int _tmain(int argc, _TCHAR* argv[])
{
son * s;//派生类的指针
grandson gs;//派生类的派生类对象
s=&gs;//派生类的指针指向其子类的地址
s->print();//函数调用
system("pause");
return 0;
}
结果调用了派生类的派生类函数
在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数。
纯虚函数是一种特殊的虚函数,它的一般格式如下:
class <类名>
{
virtual <类型><函数名>(<参数表>)=0;
…
};
在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。除非在派生类中完全实现基类中所有的的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
一般而言纯虚函数的函数体是缺省的,但是也可以给出纯虚函数的函数体(此时纯虚函数变为虚函数),这一点经常被人们忽视,调用纯虚函数的方法为baseclass::virtual function.
4、多态在理论中的概念与意义
理论中的概念:
- 静态联编
*在函数编译期间就能确定具体的函数调用
-如.函数重载 - 动态联编
*在程序实际运行后才能确定函数的具体调用
-如.函数重写
给个例子说明:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
virtual void func() //虚函数,还存在重载
{
cout << "void func()" << endl;
}
virtual void func(int i)
{
cout << "void func(int i) : " << i << endl;
}
virtual void func(int i, int j)
{
cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
}
};
class Child : public Parent
{
public:
void func(int i, int j)
{
cout << "void func(int i, int j) : " << i + j << endl;
}
void func(int i, int j, int k)
{
cout << "void func(int i, int j, int k) : " << i + j + k << endl;
}
};
void run(Parent* p)
{
p->func(1, 2); // 展现多态的特性
// 动态联编
}
int main()
{
Parent p;
p.func(); // 静态联编
p.func(1); // 静态联编
p.func(1, 2); // 静态联编
cout << endl;
Child c;
c.func(1, 2); // 静态联编
cout << endl;
run(&p);
run(&c);//虚函数,调用了子类的函数
return 0;
}
上述程序的运行结果为:
void func()
void func(int i) : 1
void func(int i, int j) : (1, 2)
void func(int i, int j) : 3
void func(int i, int j) : (1, 2)
void func(int i, int j) : 3