C++多态的理解
1 C++多态的概念
C++多态是面向对象编程中的一个重要概念,它允许同一个接口或父类引用可以指向多种实际类型,并且可以根据实际类型来调用相应的方法。多态的存在可以使程序具有更好的灵活性和可维护性,同时减少代码的重复性。
多态的实现主要基于继承、重写和对象指针或引用来实现。继承允许一个类继承另一个类的属性和方法,而重写则允许子类重新定义从父类中继承的方法,以满足自身的需求。通过使用父类引用来指向子类对象,我们可以在运行时确定实际调用的方法,从而实现多态。
在C++中,多态主要通过虚函数来实现。虚函数是在基类中使用关键字virtual声明的成员函数,它可以在派生类中被重写。通过在派生类中重写虚函数,可以改变基类中的方法实现逻辑,使其适应派生类的需求。然后,通过使用基类指针或引用指向派生类对象,可以调用虚函数,并根据实际类型来调用相应的方法。这个过程称为动态绑定或延迟绑定。
除了虚函数,构造函数重载和类型多态性也可以实现多态。构造函数多态性允许我们在创建派生类对象时调用基类构造函数,并在其中添加派生类的初始化代码。类型多态性则允许我们使用基类指针或引用调用派生类的方法,这些方法可以在运行时动态地绑定到正确的对象上。
需要注意的是,多态的实现需要适当的条件。首先,父类中必须声明虚函数,以便在派生类中进行重写。其次,父类指针或引用必须指向派生类对象,以便在运行时确定实际调用的方法。此外,多态的实现还需要注意访问修饰符、纯虚函数和抽象基类等概念的使用。
C++多态是一种重要的面向对象编程技术,它可以提高程序的灵活性和可维护性,同时减少代码的重复性。通过继承、重写和虚函数等机制,我们可以实现多态,从而让程序更加灵活、可扩展和易于维护。
2 C++多态的类型
C++中的多态性有多种不同的类型,主要包括以下几种:
编译时多态性:这种多态性是通过函数重载和运算符重载来实现的。它允许我们使用相同的函数名或操作符来定义多个版本,这些版本在编译时会根据其参数类型和个数进行选择。
运行时多态性:这种多态性是通过虚函数和基类指针或引用来实现的。它允许我们使用基类指针或引用指向子类对象,并调用子类重写后的虚函数。这种多态性也称为动态绑定或延迟绑定。
构造函数多态性:这种多态性是通过在派生类中重载基类构造函数来实现的。它允许我们在创建派生类对象时,可以调用基类的构造函数,并在其中添加派生类的初始化代码。
类型多态性:这种多态性是通过在基类中使用指针或引用调用派生类对象来实现的。它允许我们使用基类指针或引用调用派生类的方法,这些方法可以在运行时动态地绑定到正确的对象上。
泛型编程多态性:这种多态性是通过模板和泛型编程来实现的。它允许我们编写可适用于不同数据类型的代码,这些代码在编译时会根据实际数据类型进行选择。
这些多态性类型提供了不同的编程方式和工具,使我们能够更灵活地设计和实现程序。
3 虚函数实现多态案例分析
该案例的代码是基于C++多态上提供的案例去做的分析
3.1 非虚函数成员函数调用的案例
3.1.1 测试代码
需要注意的地方是基类Shape的area成员函数是未用virtual关键字去修饰成员函数的: int area()
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
cout << "base constructor class" <<endl;
}
int area() ------>基类成员函数实现未用virtual关键字修饰
{
cout << "Parent class area :" <<endl;
return 0;
}
~Shape()
{
cout << "Base DeConstructor Shape" <<endl;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) {
cout << "Rectangle constructor class" <<endl;
}
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
~Rectangle()
{
cout << " Rectangle class deconstructor "<<endl;
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) {
cout << "Triangle constructor class" <<endl;
}
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
~Triangle()
{
cout << " Triangle class deconstructor "<<endl;
}
};
// 程序的主函数
int main( )
{
Shape *shape;
cout <<" ============== 1 =============="<<endl;
Rectangle rec(10,7);
cout <<" ============== 2 =============="<<endl;
Triangle tri(10,5);
cout <<" ============== 3 =============="<<endl;
// 存储矩形的地址
shape = &rec;
cout <<" ============== 4 =============="<<endl;
// 调用矩形的求面积函数 area
shape->area();
cout <<" ============== 5 =============="<<endl;
// 存储三角形的地址
shape = &tri;
cout <<" ============== 6 =============="<<endl;
// 调用三角形的求面积函数 area
shape->area();
cout <<" ============== 7 =============="<<endl;
return 0;
}
3.1.2 测试运行结果
重点关注下面的log:
============== 4 ==============
Parent class area :
============== 5 ==============
============== 6 ==============
Parent class area :
============== 7 ==============
测试运行结果:
$ g++ base_derive_test.cpp -o demo
$ ./demo
============== 1 ==============
base constructor class
Rectangle constructor class
============== 2 ==============
base constructor class
Triangle constructor class
============== 3 ==============
============== 4 ==============
Parent class area :
============== 5 ==============
============== 6 ==============
Parent class area :
============== 7 ==============
Triangle class deconstructor
Base DeConstructor Shape
Rectangle class deconstructor
Base DeConstructor Shape
$
3.2 虚函数成员函数调用案例
3.2.1 虚函数实现测试代码
基类Shape的area成员函数是未用virtual关键字去修饰成员函数的: virtual int area()
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
cout << "base constructor class" <<endl;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
~Shape()
{
cout << "Base DeConstructor Shape" <<endl;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) {
cout << "Rectangle constructor class" <<endl;
}
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
~Rectangle()
{
cout << " Rectangle class deconstructor "<<endl;
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) {
cout << "Triangle constructor class" <<endl;
}
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
~Triangle()
{
cout << " Triangle class deconstructor "<<endl;
}
};
// 程序的主函数
int main( )
{
Shape *shape;
cout <<" ============== 1 =============="<<endl;
Rectangle rec(10,7);
cout <<" ============== 2 =============="<<endl;
Triangle tri(10,5);
cout <<" ============== 3 =============="<<endl;
// 存储矩形的地址
shape = &rec;
cout <<" ============== 4 =============="<<endl;
// 调用矩形的求面积函数 area
shape->area();
cout <<" ============== 5 =============="<<endl;
// 存储三角形的地址
shape = &tri;
cout <<" ============== 6 =============="<<endl;
// 调用三角形的求面积函数 area
shape->area();
cout <<" ============== 7 =============="<<endl;
return 0;
}
3.2.2 测试运行结果
调用成员函数erea时的结果如下所示:
============== 4 ==============
Rectangle class area :
============== 5 ==============
============== 6 ==============
Triangle class area :
============== 7 ==============
整个测试程序的运行结果:
$ g++ base_derive_test.cpp -o demo
$ ./demo
============== 1 ==============
base constructor class
Rectangle constructor class
============== 2 ==============
base constructor class
Triangle constructor class
============== 3 ==============
============== 4 ==============
Rectangle class area :
============== 5 ==============
============== 6 ==============
Triangle class area :
============== 7 ==============
Triangle class deconstructor
Base DeConstructor Shape
Rectangle class deconstructor
Base DeConstructor Shape
$
3.3 基类析构函数的实现
基类的析构函数在实现定义的时候建议采用虚函数定义的方式去声明和实现:
3.3.1 析构函数定义和实现案例
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
cout << "base constructor class" <<endl;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
virtual ~Shape()
{
cout << "Base DeConstructor Shape" <<endl;
}
};
3.3.2 基类析构函数采用虚函数实现的原因
基类析构函数采用虚函数实现主要是为了确保正确的资源清理和避免潜在的内存泄漏问题。
当使用基类指针或引用指向派生类对象时,如果析构函数不是虚函数,那么只有基类的析构函数会被调用,而派生类的析构函数不会被调用。这样会导致派生类对象中的资源无法被正确地清理,从而产生内存泄漏或其他资源泄漏问题。
如果将基类的析构函数声明为虚函数,那么在通过基类指针或引用删除一个派生类对象时,派生类的析构函数也会被正确调用。这样可以确保派生类对象中的资源被正确清理,避免内存泄漏和其他资源泄漏问题。
另外,析构函数采用虚函数实现还可以确保在派生类析构函数执行之前,基类的析构函数已经执行完毕。这样可以保证正确的析构顺序,避免因析构顺序不当而导致的问题。
因此,为了避免潜在的资源清理问题和保证正确的析构顺序,通常将基类的析构函数声明为虚函数。
3.4 纯虚函数
在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
纯虚函数的定义格式为:virtual int area() = 0;
= 0
告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
基类中定义了纯虚函数,则该基类被成为虚基类,对应的纯虚函数必须要在派生类中去实现。