在现实生活中,不同事物的同一种行为或动作,在细节上的表现可能是不一样的。比如人类,在说话这个行为上,中国人用的是汉语,英国人用的是英语,韩国人用的是韩文等。
在计算机编程语言中, 多态(polymorphism)指的是为不同数据类型的实体提供统一的接口,C++也支持多态特性。利用多态特性提供统一接口来描述以上所说的现实中的不同事物的同一种行为或动作,但在细节上的表现又可能不一样。
多态的种类
静态多态:函数重载,操作符重载,就属于静态多态。函数名相同,在相同参数个数的条件下,函数的参数类型不同,或参数的类型顺序不同,或函数参数的个数不同。注意:函数参数的返回值不同是不能作为函数重载的条件的。
动态多态:父类的成员函数声明为虚函数或纯虚函数,子类重写父类的虚函数或纯虚函数,在使用时,让子类的对象指向父类的引用或指针,从而实现运行时多态。
在C++面向对象编程中,我们说的多态,一般指的就是动态多态。
静态多态和动态多态的区别
静态多态函数地址是早绑定,编译阶段确认函数地址。
动态多态函数地址是晚绑定,运行阶段确定函数地址。
语法规则
虚函数语法
virtual 函数返回值 成员函数名(参数列表) {}
纯虚函数语法
virtual 函数返回值 成员函数名(参数列表) = 0;
1.动态多态举例:将父类的成员函数声明为虚函数,子类重写父类的虚函数
#include <iostream>
using namespace std;
class People
{
public:
virtual void speak() const //虚函数
{
cout << "人在说话" << endl;
}
};
class Chinese : public People
{
public:
virtual void speak() const //重写父类的虚函数
{
cout << "中国人在用汉语说话" << endl;
}
};
class British : public People
{
public:
virtual void speak() const //重写父类的虚函数
{
cout << "英国人在用英文说话" << endl;
}
};
void peopleSpeak(const People &p)
{
p.speak();
}
void peopleSpeak(const People *p)
{
p->speak();
}
int main(int argc, char * argv[])
{
Chinese c;
peopleSpeak(c); //子类对象指向父类的引用
peopleSpeak(&c); //子类对象指向父类的指针
British b;
peopleSpeak(b); //子类对象指向父类的引用
peopleSpeak(&b); //子类对象指向父类的指针
cout << "sizeof(People) = " << sizeof(People) << endl;
cout << "sizeof(Chinese) = " << sizeof(Chinese) << endl;
cout << "sizeof(British) = " << sizeof(British) << endl;
return 0;
}
分析:C++中,空类占用内存大小为1字节。类的成员变量和成员函数是分开存储的,只有非静态成员变量才存储在类中或类的对象上。通过该类创建的所有对象都共享同一个函数。但我们打印出的只含有一个虚函数的People,Chinese,British类占用了8个字节。这是为什么呢?本质是因为:含有虚函数,纯虚函数的类会有一个虚函数表指针,这个虚函数指针指向虚函数表。因为我编译的是64位可执行程序,一个函数指针的内存大小为8字节,所以打印出含有虚函数的People,Chinese,British类的内存大小是8个字节
2.动态多态举例:将父类的成员函数声明为纯虚函数,子类重写父类的纯虚函数
#include <iostream>
using namespace std;
class People
{
public:
virtual void speak() const = 0; //纯虚函数
};
class Chinese : public People
{
public:
virtual void speak() const //重写父类的纯虚函数
{
cout << "中国人在用汉语说话" << endl;
}
};
class British : public People
{
public:
virtual void speak() const //重写父类的纯虚函数
{
cout << "英国人在用英文说话" << endl;
}
};
void peopleSpeak(const People &p)
{
p.speak();
}
void peopleSpeak(const People *p)
{
p->speak();
}
int main(int argc, char * argv[])
{
Chinese c;
peopleSpeak(c); //子类对象指向父类的引用
peopleSpeak(&c); //子类对象指向父类的指针
British b;
peopleSpeak(b); //子类对象指向父类的引用
peopleSpeak(&b); //子类对象指向父类的指针
cout << "sizeof(People) = " << sizeof(People) << endl;
cout << "sizeof(Chinese) = " << sizeof(Chinese) << endl;
cout << "sizeof(British) = " << sizeof(British) << endl;
return 0;
}
抽象类
含有纯虚函数的类称为抽象类。
抽象类的特点:
无法实例化
子类必须重写抽象类中的纯虚函数,否则这个子类还是抽象类
抽象类虽然不能实例化,但抽象类的虚函数表指针是存在的
#include <iostream>
using namespace std;
class People
{
public:
virtual void speak() const = 0; //纯虚函数
};
int main(int argc, char * argv[])
{
//People p; //错误:抽象类不能实例化
cout << "sizeof(People) = " << sizeof(People) << endl; //sizeof(People) = 8
return 0;
}
虚析构函数和纯虚析构函数
在C++中使用多态时,如果在堆区new子类对象指向父类指针,那么父类指针在释放时,是调用不到子类的析构函数的,为了解决这个问题,需要把父类的析构函数定义为虚析构函数或纯虚析构函数。
虚析构函数语法
virtual ~类名(){}
纯虚析构函数
virtual ~类名() = 0; //类内声明
类名::~类名(){} //类外必须实现
含有纯虚析构函数的类也是抽象类,无法实例化对象
举例:堆区new子类对象指向父类指针,那么父类指针在释放时,是调用不到子类的析构函数
#include <iostream>
using namespace std;
class People
{
public:
~People()
{
cout << "~People()" << endl;
}
virtual void speak() const = 0; //纯虚函数
};
class Chinese : public People
{
public:
~Chinese()
{
cout << "~Chinese()" << endl;
}
virtual void speak() const //重写父类的纯虚函数
{
cout << "中国人在用汉语说话" << endl;
}
};
class British : public People
{
public:
~British()
{
cout << "~British()" << endl;
}
virtual void speak() const //重写父类的纯虚函数
{
cout << "英国人在用英文说话" << endl;
}
};
void peopleSpeak(const People &p)
{
p.speak();
}
void peopleSpeak(const People *p)
{
p->speak();
}
int main(int argc, char * argv[])
{
People *p = new Chinese;
peopleSpeak(*p); //子类对象指向父类的引用
peopleSpeak(p); //子类对象指向父类的指针
delete p;
p =NULL;
p = new British;
peopleSpeak(*p); //子类对象指向父类的引用
peopleSpeak(p); //子类对象指向父类的指针
delete p;
p = NULL;
return 0;
}
举例:将父类的析构函数定义为虚析构函数,解决通过父类指针释放子类对象调用不到子类的析构函数的问题
#include <iostream>
using namespace std;
class People
{
public:
virtual ~People() //将父类的析构函数定义为虚析构函数
{
cout << "~People()" << endl;
}
virtual void speak() const = 0; //纯虚函数
};
class Chinese : public People
{
public:
virtual ~Chinese()
{
cout << "~Chinese()" << endl;
}
virtual void speak() const //重写父类的纯虚函数
{
cout << "中国人在用汉语说话" << endl;
}
};
class British : public People
{
public:
virtual ~British()
{
cout << "~British()" << endl;
}
virtual void speak() const //重写父类的纯虚函数
{
cout << "英国人在用英文说话" << endl;
}
};
void peopleSpeak(const People &p)
{
p.speak();
}
void peopleSpeak(const People *p)
{
p->speak();
}
int main(int argc, char * argv[])
{
People *p = new Chinese;
peopleSpeak(*p); //子类对象指向父类的引用
peopleSpeak(p); //子类对象指向父类的指针
delete p;
p =NULL;
p = new British;
peopleSpeak(*p); //子类对象指向父类的引用
peopleSpeak(p); //子类对象指向父类的指针
delete p;
p = NULL;
cout << "sizeof(People) = " << sizeof(People) << endl;
cout << "sizeof(Chinese) = " << sizeof(Chinese) << endl;
cout << "sizeof(British) = " << sizeof(British) << endl;
return 0;
}
举例:将父类的析构函数定义为纯虚析构函数,解决通过父类指针释放子类对象调用不到子类的析构函数的问题
#include <iostream>
using namespace std;
class People
{
public:
virtual ~People() = 0; //将父类的析构函数声明为纯虚析构函数
virtual void speak() const = 0; //纯虚函数
};
People::~People() //类外必须初始化
{
cout << "~People()" << endl;
}
class Chinese : public People
{
public:
virtual ~Chinese()
{
cout << "~Chinese()" << endl;
}
virtual void speak() const //重写父类的纯虚函数
{
cout << "中国人在用汉语说话" << endl;
}
};
class British : public People
{
public:
virtual ~British()
{
cout << "~British()" << endl;
}
virtual void speak() const //重写父类的纯虚函数
{
cout << "英国人在用英文说话" << endl;
}
};
void peopleSpeak(const People &p)
{
p.speak();
}
void peopleSpeak(const People *p)
{
p->speak();
}
int main(int argc, char * argv[])
{
People *p = new Chinese;
peopleSpeak(*p); //子类对象指向父类的引用
peopleSpeak(p); //子类对象指向父类的指针
delete p;
p =NULL;
p = new British;
peopleSpeak(*p); //子类对象指向父类的引用
peopleSpeak(p); //子类对象指向父类的指针
delete p;
p = NULL;
cout << "sizeof(People) = " << sizeof(People) << endl;
cout << "sizeof(Chinese) = " << sizeof(Chinese) << endl;
cout << "sizeof(British) = " << sizeof(British) << endl;
return 0;
}
关于C++多态的知识点梳理以及使用,先写到这,后面有空再写一篇C++多态的内存模型,让我们更加深层次的去了解C++多态的实现原理。