工作了好多年,才想起来写一点笔记,我也是醉了。。。
马上面临面试,所以复习一下知识点,好多要点,毕竟这是个看面试不看工作能力给工资的时代=。=
面向对象语言的基本特性:
- 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected,public)
- 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。
- 多态:系统能够在运行时,能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性。
本文参考帖子:http://www.cnblogs.com/fangyukuan/archive/2010/05/30/1747449.html
不言而喻,多态是一个很重要的知识点,我们直接上代码:
//例程1
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed=speed;
Vehicle::total=total;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<endl;
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird=aird;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
protected:
int aird;
};
void main()
{
Vehicle a(120,4);
a.ShowMember();
Car b(180,110,4);
b.ShowMember();
cin.get();
}
C++中允许派生类重载基类成员函数,对于重载来说,不同的类对象调用相应的成员函数,即系统知道a.ShowMember()调用的是Vehicle的成员方法,b.ShowMember()调用的是Car的成员方法。
但是实际使用中,会遇到这样的情况:
//例程2
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed=speed;
Vehicle::total=total;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<endl;
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird=aird;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
protected:
int aird;
};
void test(Vehicle &temp)
{
temp.ShowMember();
}
void main()
{
Vehicle a(120,4);
Car b(180,110,4);
test(a);
test(b);
cin.get();
}
在这种情况下,因为Vehicle是基类,所以Car类包含了Vehicle的所有属性和方法,所以test()调用是没有问题的,但是我们想要实现的效果是传入不同的对象调用对应对象类的方法,但是实际上这个例子中,系统分不清你传给test()的是哪个类的对象,所以他们都会调用基类的ShowMember()函数。
为了解决这个问题,就要用到多态来解决这个问题了:对于例程序1,这种能够在编译时就能够确定哪个重载的成员函数被调用的情况被称做先期联编(early binding),而在系统能够在运行时,能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性,或叫滞后联编(late binding)
//例程3
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed = speed;
Vehicle::total = total;
}
virtual void ShowMember()//虚函数
{
cout<<speed<<"|"<<total<<endl;
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird = aird;
}
virtual void ShowMember()//虚函数,在派生类中,由于继承的关系,这里的virtual也可以不加
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
public:
int aird;
};
void test(Vehicle &temp)
{
temp.ShowMember();
}
int main()
{
Vehicle a(120,4);
Car b(180,110,4);
test(a);
test(b);
cin.get();
}
C++中的多态依赖于虚函数,关键字virtual;而java中用到的是abstract;
只要基类声明是virtual,那么子类就无需再声明了,系统会自动添加该关键字,但是为了可读性最好还是写上吧。
虚函数定义需要遵循一下几个规则:
- 如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。
- 只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。
- 静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。
- 内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义,但是在编译的时候系统仍然将它看做是非内联的。
- 构造函数不能是虚函数,因为构造的时候,对象还是一片未定型的空间,只有构造完成后,对象才是具体类的实例。
- 析构函数可以是虚函数,而且通常声名为虚函数。
析构函数为什么也要声明为虚函数:
#include "stdafx.h"
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed=speed;
Vehicle::total=total;
}
virtual void ShowMember()
{
cout<<speed<<"|"<<total<<endl;
}
virtual ~Vehicle()
{
cout<<"载入Vehicle基类析构函数"<<endl;
cin.get();
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird=aird;
}
virtual void ShowMember()
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
virtual ~Car()
{
cout<<"载入Car派生类析构函数"<<endl;
cin.get();
}
protected:
int aird;
};
void test(Vehicle &temp)
{
temp.ShowMember();
}
void DelPN(Vehicle *temp)
{
delete temp;
}
void main()
{
Car *a=new Car(100,1,1);
a->ShowMember();
DelPN(a);
cin.get();
}
从上例代码的运行结果来看,当调用DelPN(a);后,在析构的时候,系统成功的确定了先调用Car类的析构函数,而如果将析构函数的virtual修饰去掉,再观察结果,会发现析构的时候,始终只调用了基类的析构函数,由此我们发现,多态的特性的virtual修饰,不单单对基类和派生类的普通成员函数有必要,而且对于基类和派生类的析构函数同样重要。