写在前面:作为一只小白,感谢小甲鱼老师提供这么好的入门课程。因此在这里做个笔记,如有侵权请联系删除
www.fishc.com
https://blog.csdn.net/qq_30708445/article/details/88720305
1、课前练习(虚函数的使用):
#include <iostream>
using namespace std;
class animal
{
public:
void sleep()
{
cout << "animal sleep" << endl;
}
virtual void breathe() //利用虚函数使得子类的方法能顺利覆盖父类的方法
//void breathe()
{
cout << "animal breathe" << endl;
}
};
class fish:public animal
{
public:
void breathe()
{
cout << "fish bubble" << endl;
}
};
int main()
{
fish fh;
animal *pAn = &fh;
pAn -> breathe();
return 0;
}
运行结果:
fish bubble
2、抽象方法(纯虚函数):
抽象方法的语法:在声明一个虚方法的基础上,在原型的末尾加上“=0”。(告诉编译器不要浪费时间在这个类里寻找这个方法的实现)
#include <iostream>
#include <string>
using namespace std;
class Pet
{
public:
Pet(string theName);
virtual void eat(); //虚函数
virtual void sleep();
virtual void play() = 0; //抽象方法
protected:
string name;
};
class Cat:public Pet
{
public:
Cat(string theName);
void climb();
void play();
};
class Dog:public Pet
{
public:
Dog(string theName);
void bark();
void play();
};
Pet::Pet(string theName)
{
name = theName;
}
void Pet::eat()
{
cout << name << "正在吃东西!" << endl;
}
void Pet::sleep()
{
cout << name << "正在睡觉!" << endl;
}
Cat::Cat(string theName):Pet(theName)
{
}
void Cat::climb()
{
cout << name << "正在爬树!" << endl;
}
void Cat::play()
{
cout << name << "玩毛线球!" << endl;
}
Dog::Dog(string theName):Pet(theName)
{
}
void Dog::bark()
{
cout << name << "旺旺!\n";
}
void Dog::play()
{
cout << name << "正在追赶那只该死的猫!" << endl;
}
int main()
{
Pet *cat = new Cat("加菲");
Pet *dog = new Dog("欧迪");
cat -> sleep();
cat -> eat();
cat -> play();
dog -> sleep();
dog -> eat();
dog -> play();
delete cat;
delete dog;
return 0;
}
加菲正在睡觉!
加菲正在吃东西!
加菲玩毛线球!
欧迪正在睡觉!
欧迪正在吃东西!
欧迪正在追赶那只该死的猫!
3、多态性
多态性是指用一个名字定义不同的函数,调用同一个名字的函数,却执行不同的操作,从而实现传说中的“一个接口,多种方法”。
多态是如何实现绑定的?
---编译时的多态性:通过重载实现
---运行时的多态性:通过虚函数实现
编译时的多态性特点是运行速度快,运行时的特点是高度灵活和抽象。
4、析构函数为什么都是虚方法?:
#include <iostream>
using namespace std;
class ClxBase
{
public:
ClxBase()
{
};
virtual ~ClxBase()
{
};
virtual void doSomething()
{
cout << "Do something in class ClsBase!\n";
}
};
class ClxDerived:public ClxBase
{
public:
ClxDerived()
{
};
~ClxDerived()
{
cout << "Output from hte destructor of class ClxDerived!\n";
};
void doSomething()
{
cout << "Do something in class ClxDerived!\n";
};
};
int main()
{
ClxBase *ptrClx = new ClxDerived;
ptrClx -> doSomething();
delete ptrClx;
return 0;
}
运行结果
Output from hte destructor of class ClxDerived!
对于上述例子,如果我们把类ClxBase析构函数前的virtual去掉,那输出结果就应该是:
---Do something in class ClxDerived!
也就是说ClxDerived的析构函数根本没有被调用,而一般下类的析构函数里面都是释放内存资源,而析构函数不被调用的话会造成内存泄露。因此,析构器都是虚方法,是为了当一个基类的指针删除一个派生类的对象时,派生类的析构函数可以被正确调用。
另外,当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面存放着虚函数指针。为了节省资源,只有当一个类被用来作为基类的时候,我们才把析构函数写成虚函数。
复习:
重载与覆盖
(1)成员函数被重载的特征:
a、相同的范围(在同一个类中);
b、函数名字相同;
c、参数不同;
d、virtual关键字可有可无。
(2)覆盖是指派生类函数覆盖基类函数,特征是:
a、不同的范围(分别位于派生类与基类);
b、函数名字相同;
c、参数相同;
d、基类函数必须有virtual关键字