学习c++这么久,c++的几个特点:封装,继承,多态。
封装很好理解,是面向对象程序设计语言的标志,把数据和函数封装在一起作为一个类的定义,对象就是类的一个实例化,类的实际内部都被隐藏起来,外部只需要通过实例化一个对象,然后通过对象调用类的内部成员函数就可以了,封装可以使得代码模块化,避免代码的重写;
继承可以扩展已存在的代码,跟封装目的相同,都是为了代码重用,简单的定义一个父类,就可以使用父类的成员函数(分为公开,私有和受保护)而不用自己重新定义,非常方便;
最让我纠结的是类的多态,一直说多态性,也会用,无非就是在父类定义一个虚函数,然后在子类中重定义这个函数,真正调用的时候还是通过子类的对象调用这个函数,于是一直不明白这个多态性强大在哪,今天好好研究了一下,才算有了点眉目。
多态性分为2种,编译时多态性和运行时多态性。前者通过重载函数实现,后者通过虚函数实现。指相同的对象(父类对象)收到不同的消息(指向不同的子类)或者不同的对象(子类对象)接受相同的消息产生不同的实现动作。
多态与非多态的实质区别就在于函数地址是早绑定还是晚绑定,即在编译期间函数调用的地址是编译时就确定就是早绑定,在运行时才能确定就是晚绑定。
下面是一个很简单的测试栗子,继承无需多说,
p->duotai(); p->duotai1();
这两句能很好的体现出多态的特点,p是父类的一个对象,如果他指向的也是一个父类的对象,那没什么好说的输出的调用的肯定是父类的成员函数,输出的是"parent duotai"。而如果父类指向的是子类的对象,则正体现了多态的用法。由于指针是个父类指针,在调用duotai1()的时候,调用的不是虚函数,而是指向一个固定偏移量的函数的时候(属于早绑定),调用的是父类的duotai1,输出的是"parent duotai1",而对于虚函数duotai(),每个虚函数都有一个虚函数列表,系统会根据指向对象的不同,通过函数序列找到相应对象的地址,调用对用的成员函数。
总结来说,对于非虚函数,调用是根据对象属于哪一类,则调用哪一类的成员函数;而对于虚函数,则是根据对象指向的对象,指向哪个类的对象,则调用哪个类的成员函数,例如
B* ptr = (B*)&a;
ptr->duotai();
ptr->duotai1();
B的一个对象虽然指向父类A,但是由于duotai1()不是虚函数,调用的时候还是调用的子类(B类)的函数,而对于虚函数duotai(),则调用的是指向的A类的成员函数。
#include"iostream"
using namespace std;
class A
{
public:
void jicheng()
{
cout<<"jicheng"<<endl;
}
virtual void duotai()
{
cout<<"parent duotai"<<endl;
}
void duotai1()
{
cout<<"parent duotai1"<<endl;
}
};
class B:public A
{
public:
void duotai()
{
cout<<"child duotai"<<endl;
}
void duotai1()
{
cout<<"child duotai1"<<endl;
}
};
int main()
{
B b;
b.jicheng();
A* p=&b;
p->duotai1();
p->duotai();
return 0;
}
最后,有一种特殊的虚函数是纯虚函数,在父类中是没有定义的,具体表现为 virtual void duotai()=0,对于纯虚函数,编译器要求在子类中必须重写以实现多态性。同时含有纯虚函数的类称为抽象类,不能生成对象。
比如我定义了一个父类,动物类,这是一个抽象类,我在里面定义一个eatfood()的成员函数,每一个动物都要吃东西,再定义一个猴子类,猫类等继承动物类的子类,再各个子类下面都有一个eatfood()的函数,但是实现是不同的。系统会根据你指向对象的不同,调用不同的成员函数,产生不同的结果,既方便又明了。
总体来说,多态的优点就是无论传递过来不的是哪个类的对象,函数都能够通过同一个接口调用适应各自对象的实现方法。
参考:http://blog.youkuaiyun.com/hackbuteer1/article/details/7475622 ,纯手打,加上一些自己的理解,希望会对一些同样迷茫的人有点帮助。。
成功之路,细节决定成败,
看了网上这么多大神的帖子,真是觉得未来要走的路还很长。