一.问题引出
如果子类中定义了与父类中原型相同的函数会发生什么?
函数重写:在子类中定义与父类中原型相同的函数,函数重写只发生在父类与子类之间。
测试代码如下
class parent
{
private:
int a;
public:
parent(int a)
{
this->a = a;
}
void print()
{
cout << "a=" << a << endl;
}
};
class child :public parent
{
private:
int b;
public:
child(int b) :parent(10)
{
this->b = b;
}
void print()
{
cout << "b=" << endl;
}
};
void test(parent *p,parent &n)
{
p->print();
n.print();
}
int main()
{
parent c1(20);
child c2(30);
//1.指针
parent *p = NULL;
p = &c2;
p->print();
//2.引用
parent &n = c2;
n.print();
//3.指针,引用参数
test(&c2,c2);
system("pause");
return 0;
}
打印结果如下
由此可见调用的都是父类的print()函数。
这并不是我们想要的结果。
我们希望是:
1.根据实际对象类型来判断重写函数的调用
2.如果父类指针指向父类,则调用父类函数,如果父类指针指向子类则调用子类函数
我们所希望的其实就是多态了!如下图
则怎么实现多态呢?如下图
即在重写函数前面加上virtual关键字,注意在父类前面写上了virtual关键字以后,在子类中写不写都行,不写也默认为写了,但是一般情况下写上比较好!
class parent
{
private:
int a;
public:
parent(int a)
{
this->a = a;
}
void virtual print()
{
cout << "a=" << a << endl;
}
};
class child :public parent
{
private:
int b;
public:
child(int b) :parent(10)
{
this->b = b;
}
void print()
{
cout << "b=" <<b<< endl;
}
};
void test(parent *p,parent &n)
{
p->print();
n.print();
}
int main()
{
parent c1(20);
child c2(30);
//1.指针
parent *p = NULL;
p = &c2;
p->print();
//2.引用
parent &n = c2;
n.print();
//3.指针,引用参数
test(&c2,c2);
system("pause");
return 0;
}
这样就如我们所愿打印子类成员了!
接下来举一个例字来终点说明多态在编程中的重要性
背景如下:
代码实现:
class CombatA
{
public:
int damage()
{
return 10;
}
};
class CombatA2:public CombatA
{
public:
int damage()
{
return 20;
}
};
class CombatB
{
public:
int damage()
{
return 15;
}
};
int main()
{
CombatA one;
CombatA2 two;
CombatB foe;
if (one.damage() > foe.damage())
{
cout << "我军战机胜" << endl;
}
else
{
cout << "敌军战机胜" << endl;
}
if (two.damage() > foe.damage())
{
cout << "我军战机胜" << endl;
}
else
{
cout << "敌军战机胜" << endl;
}
system("pause");
return 0;
}
接下来我们用多态去写这段代码:
class CombatA
{
public:
int virtual damage()
{
return 10;
}
};
class CombatA2:public CombatA
{
public:
int virtual damage()
{
return 20;
}
};
class CombatB
{
public:
int virtual damage()
{
return 15;
}
};
void test(CombatA *bat, CombatB *bats)//给对象搭建的舞台
{
if (bat->damage() > bats->damage())
{
cout << "我军战机胜" << endl;
}
else
{
cout << "敌军战机胜" << endl;
}
}
int main()
{
CombatA one;
CombatA2 two;
CombatB foe;
test(&one, &foe);
test(&two, &foe);
system("pause");
return 0;
}
当然比如后面我在升级第三代战机 ,从一代战机继承,一样可以调用test函数去用,如下:
class CombatA
{
public:
int virtual damage()
{
return 10;
}
};
class CombatA2:public CombatA
{
public:
int virtual damage()
{
return 20;
}
};
class CombatA3 :public CombatA
{
int virtual damage()
{
return 30;
}
};
class CombatB
{
public:
int virtual damage()
{
return 15;
}
};
void test(CombatA *bat, CombatB *bats)//给对象搭建的舞台
{
if (bat->damage() > bats->damage())
{
cout << "我军战机胜" << endl;
}
else
{
cout << "敌军战机胜" << endl;
}
}
int main()
{
CombatA one;
CombatA2 two;
CombatB foe;
CombatA3 three;
test(&one, &foe);
test(&two, &foe);
test(&three, &foe);
system("pause");
return 0;
}
接下来我说说多态的思想:
面向对象三个概念:
封装:突破C函数的概念,用类做函数参数的时候可以使用对象的属性和对象的方法
继承:A,B代码重用
多态:可以使用未来,比如刚才的第三代战机就可以用test函数
实现多态的三个条件:
1.要有继承,父类子类
2.要有虚函数重写
3.就是比如刚才的test函数所搭建的舞台(用父类指针指向子类对象)
下面说说关于多态的理论基础:
一.静态联编和动态联编
1.联编:指一个程序模块,代码之间互相关联的过程
2.静态联编:是程序的匹配,连续在编译阶段实现,也称为早起匹配(重载函数使用静态联编)
3.动态联编:指程序联编推迟到运行进行时,所以又称为晚期联编,迟绑定!如:switch语句和if语句是动态联编
不写virtual关键字,编译器实行的是静态联编,不管因为是父类类型所以不管参数类型是什么在编译阶段就确定执行父类的函数了,而动态联编就是在运行时根据不同对象决定调用哪个函数!
二.虚析构函数
1.构造函数不能是虚函数,建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数
2.析构函数可以是虚的,虚析构函数用于指引 delete运算符的正确析构动态对象
接下来看如下代码:
class A
{
private:
char *p;
public:
A()
{
p = new char[20];
strcpy(p, "hello,A");
}
~A()
{
delete [] p;
cout << "A的析构函数" << endl;
}
};
class B:public A
{
private:
char *p;
public:
B()
{
p = new char[20];
strcpy(p, "hello,B");
}
~B()
{
delete [] p;
cout << "B的析构函数" << endl;
}
};
class C :public B
{
private:
char *p;
public:
C()
{
p = new char[20];
strcpy(p, "hello,C");
}
~C()
{
delete [] p;
cout << "C的析构函数" << endl;
}
};
void fun(A *base)
{
delete base;
}
int main()
{
A *pt = new C;
fun(pt);
system("pause");
return 0;
}
运行结果如下:
只执行了A类的析构函数,这样会造成内存泄露~
在析构函数前面加上virtual关键字,就是虚析构函数了;
class A
{
private:
char *p;
public:
A()
{
p = new char[20];
strcpy(p, "hello,A");
}
virtual ~A()
{
delete [] p;
cout << "A的析构函数" << endl;
}
};
class B:public A
{
private:
char *p;
public:
B()
{
p = new char[20];
strcpy(p, "hello,B");
}
virtual ~B()
{
delete [] p;
cout << "B的析构函数" << endl;
}
};
class C :public B
{
private:
char *p;
public:
C()
{
p = new char[20];
strcpy(p, "hello,C");
}
virtual ~C()
{
delete [] p;
cout << "C的析构函数" << endl;
}
};
void fun(A *base)
{
delete base;
}
int main()
{
A *pt = new C;
fun(pt);
system("pause");
return 0;
}
运行界面如下:
这样就不会内存泄露了
//通过父类指针,把所有子类对象的析构函数都执行一遍,通过父类指针,释放所有子类资源。