标签:
//C++ 虚函数、静态联编和动态联编、抽象类
#include<iostream>
#include<string>
using namespace std;
class People
{
private:
string name;
int age;
public:
People(string name_, int age_):name(name_), age(age_){}
People(){cout << "基类默认构造函数" << endl;}
//~People(){cout << "基类析构函数" << endl;} //没有声明为虚函数的析构函数
virtual ~People(){cout << "基类析构函数" << endl;} //声明为虚函数的析构函数
virtual void show()
{
cout << name << " " << age << endl;
}
};
class Student:public People
{
private:
int grade;
public:
Student(string name_, int age_, int grade_):People(name_, age_), grade(grade_){}
Student(){cout << "派生类默认构造函数" << endl;}
virtual ~Student(){cout << "派生类析构函数" << endl;} //当基类函数声明为虚函数时,派生类函数加不加virtual关键字都行,但是最好加上,可以提示此函数为虚函数
virtual void show()
{
cout << grade << endl;
}
};
class Animal
{
private:
string name;
public:
virtual void shout() = 0;
virtual ~Animal(){}
};
class Dog:public Animal
{
public:
virtual void shout()
{
cout << "汪汪" << endl;
}
};
class Mao:public Animal
{
public:
virtual void shout()
{
cout << "喵" << endl;
}
};
int main()
{
//正常声明类变量,当变量过期时,先调用派生类析构函数,再调用基类析构函数。
//Student s1{"linukey", 20, 100};
//s1.show();
//当用基类的指针开辟派生类的空间时,如果析构函数没有声明为虚函数,则只调用指针类型的析构函数,此时造成内存泄露
//当基类的析构函数声明为虚函数时,则此时变量过期时,先调用派生类的析构函数,再调用基类的虚构函数
//People* p = new Student{"linukey", 20, 100};
//p->show();
//delete p;
//当派生类函数要重写基类方法时,基类函数没有声明为虚函数时,则用基类指针开辟的派生类对象在调用方法时,是调用指针类型类的方法
//当基类函数声明为虚函数时,则调用实际开辟内存类型类的方法
//People* p = new Student{"linukey", 20, 100};
//p->show();
//delete p;
//如果派生类需要重写基类的方法,则需要把派生类的方法声明为虚函数,这样当基类的指针指向派生类的变量,通过指针调用方法时,就不会出现调用基类方法的错误了
//Student s2{"linukey", 20, 100};
//People* p = &s2;
//p->show();
//向上强制转换 和 向下强制转换
//Student s1;
//People* p = &s1; //向上强制转换
//People p1;
//Student* s2 = (Student*)&p1; //向下强制转换
//如果类需要被继承,则基类的析构函数应该声明为虚函数,这样就在一定程度上避免内存泄露
//如果基类中的方法需要再派生类中被重写,则应该把基类中的方法声明为虚函数,如果基类中的方法不需要被重写,则不应该声明为虚函数,否则会加大内存的开销和降低效率
/*
函数联编:将源代码中的函数调用解释为执行特定函数代码块被称为函数名联编;
早期联编/静态联编:在编译过程中进行联编被称为早起联编;
晚起联编/动态联编:因为虚函数的缘故,导致我们无法在编译过程中确认具体对象的类型,所以我们对于虚函数使用动态联编
*/
/*
虚函数表:
动态联编的类会给对象额外增加一个隐藏成员,隐藏成员中保存一个指向函数地址数组的指针,这种数组称为虚函数表;
基类和派生类都包含一个指向各自函数地址数组的指针,如果基类的函数被声明为虚函数,而派生类中没有重写,则派生类的虚函数表中的函数地址依然为基类的,
如果派生类进行了重写,则派生类的虚函数表中的函数地址则为重写的新函数的地址
*/
/*
抽象类正如它的名字,是通过几个派生类的联系而抽象出来的一个基类,这个类可以继承其他的抽象类。因为几个派生类之间的关系有些方法无法给出显示的实现,
所以我们只能定义一个方法而无法实现,我们用纯虚函数来定义这个方法。纯虚函数定义的方法虽然可以在基类中给出实现,但是必须在派生类中中给出实现。
抽象类不能有实例化对象,但是能通过抽象类的指针开辟派生类的对象。抽象类可以包含普通方法,但是必须要包含至少一个纯虚方法
*/
//正如Dog和Mao两个类,虽然都为动物,但是叫声不同,我们再基类中无法给出具体的叫声,所以我们定义一个抽象类,给出一个纯虚函数的叫声接口,由派生类去实现具体的方法
//Dog d;
//d.shout();
//Mao m;
//m.shout();
//可以通过抽象类的指针来开辟派生类对象
//Animal* a = new Dog;
//a->shout();
return 0;
}
标签:
原文地址:http://blog.csdn.net/linukey/article/details/45932807