扬州市住房和建设局网站,如何在电脑上建立网站,喀什网站建设公司,wordpress 混合移动app面向对象的多态 一、概念二、实现1. 静态多态1.1 函数重载1.2 运算符重载 2. 动态多态2.1 虚函数2.2 纯虚函数 三、虚函数1. 定义2. 实现3. 注意 四、纯虚函数1. 定义2. 作用 五、虚析构函数1. 定义2. 作用 六、 抽象类七、实现多态的注意事项1. 基类虚函数必须使用 virtual 关… 面向对象的多态 一、概念二、实现1. 静态多态1.1 函数重载1.2 运算符重载 2. 动态多态2.1 虚函数2.2 纯虚函数 三、虚函数1. 定义2. 实现3. 注意 四、纯虚函数1. 定义2. 作用 五、虚析构函数1. 定义2. 作用 六、 抽象类七、实现多态的注意事项1. 基类虚函数必须使用 virtual 关键字声明2. 子类重写基类的虚函数时必须保持函数签名的一致性3. 在析构函数中使用 virtual 关键字声明4. 在使用虚继承时需要注意继承的正确性5. 在使用抽象类时需要让子类去实现纯虚函数 八、 小结 一、概念
在面向对象编程中多态Polymorphism是一种非常重要的概念。 可以被定义为“同一种类型的变量在不同情况下表现出不同的行为”。 多态性是实现灵活的编程方式的一种方式它有以下好处
增强程序的可扩展性由于多态性允许开发人员在运行时选择使用哪个子类对象来调用函数因此可以更轻松地在需要时添加新的子类。提高程序的可维护性程序的不同部分可以互相独立地编写和维护而不需要知道具体的实现细节。代码的可读性得到改善由于多态性是在整个程序中通过使用相同的基类来调用函数来实现的因此程序的结构更加清晰简单。
在C 中可以使用虚函数实现多态性。 下面是一个简单的示例
#include iostream
using namespace std;// 定义形状基类
class Shape {public:// 定义一个纯虚函数没有具体的实现virtual int area() 0;
};// 定义矩形类
class Rectangle : public Shape {public:// 实现 Shape 类中的纯虚函数int area () { cout Rectangle 类的面积是 endl;return (width * height); }private:int width;int height;
};// 定义三角形类
class Triangle : public Shape{public:// 实现 Shape 类中的纯虚函数int area () { cout Triangle 类的面积是 endl;return (width * height / 2); }private:int width;int height;
};// 程序的主函数
int main( ) {Shape *shape;Rectangle rec;Triangle tri;// 存储矩形的地址shape rec;// 调用矩形的 area 函数shape-area();// 存储三角形的地址shape tri;// 调用三角形的 area 函数shape-area();return 0;
}在上面的示例代码中定义了一个基类 Shape其中包含一个纯虚函数 area它没有具体的实现。定义了两个子类 Rectangle 和 Triangle并在这两个子类中重新定义了 area 函数并给出了具体的实现。最后在主函数中我们声明了一个指向 Shape 类对象的指针然后将它分别指向 Rectangle 和 Triangle 对象并通过指针调用了各自的 area 函数这就实现了多态。
二、实现
多态是面向对象编程的一个重要概念在C中多态可以通过静态多态和动态多态两种方式来实现。
1. 静态多态
静态多态是在编译期间就确定的多态性主要有两种形式函数重载和运算符重载。
1.1 函数重载
函数重载是指在同一作用域内定义多个同名函数但它们的参数列表不同从而让这些函数可以根据传入的参数类型和数量进行区分。
下面是一个函数重载的例子定义了两个同名的函数 add()但是一个参数是整型另一个参数是浮点型
#include iostream
using namespace std;int add(int a, int b) {return a b;
}double add(double a, double b) {return a b;
}int main() {cout add(1, 2) endl; // 输出 3cout add(1.2, 2.3) endl; // 输出 3.5return 0;
}1.2 运算符重载
运算符重载是指在类中重载运算符使它们能够作用于该类的对象。在重载运算符时需要使用关键字 operator 来声明。
下面是一个重载加号运算符的例子定义了一个 Box 类并在其中重载了加号运算符
#include iostream
using namespace std;class Box {public:double getVolume(void) {return length * breadth * height;}void setLength(double len) {length len;}void setBreadth(double bre) {breadth bre;}void setHeight(double hei) {height hei;}// 重载加号运算符Box operator(const Box b) {Box box;box.length this-length b.length;box.breadth this-breadth b.breadth;box.height this-height b.height;return box;}private:double length; // 长double breadth; // 宽double height; // 高
};int main() {Box Box1; // 声明 Box1类型为 BoxBox Box2; // 声明 Box2类型为 BoxBox Box3; // 声明 Box3类型为 Boxdouble volume 0.0; // 把体积存储在该变量中// Box1 的详述Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0);// Box2 的详述Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0);// 计算 Box1 的体积volume Box1.getVolume();cout Box1 的体积 volume endl;// 计算 Box2 的体积volume Box2.getVolume();cout Box2 的体积 volume endl;// 把两个对象相加得到 Box3Box3 Box1 Box2;// 计算 Box3 的体积volume Box3.getVolume();cout Box3 的体积 volume endl;return 0;
}2. 动态多态
动态多态是在运行期间通过虚函数实现的多态性让同名的虚函数在不同的子类中有不同的实现。动态多态需要使用关键字 virtual 来声明虚函数。
2.1 虚函数
虚函数是在基类中定义的一个函数子类可以通过重写这个函数实现多态。当基类的指针或引用指向某个子类的对象时调用这个对象的同名虚函数实际上是调用子类中的虚函数。
下面是一个虚函数的例子先定义了一个 Shape 基类并在其中声明了一个虚函数 area然后分别定义了 Rectangle 和 Circle 两个子类并在这两个子类中重新定义了 area 函数
#include iostream
using namespace std;class Shape {public:// 声明虚函数 areavirtual int area() {cout Shape 的 area 函数 endl;}
};// 矩形子类
class Rectangle: public Shape {public:int area() { cout Rectangle 的 area 函数 endl;return (width * height); }private:int width;int height;
};// 圆形子类
class Circle: public Shape {public:int area() { cout Circle 的 area 函数 endl;return (radius * radius * 3.1415926); }private:int radius;
};int main() {Shape *shape; // 声明一个指向基类对象的指针Rectangle rec; // 声明矩形对象Circle cir; // 声明圆形对象shape rec; // 给指针赋值指向矩形对象shape-area(); // 调用矩形类的 area 函数shape cir; // 给指针赋值指向圆形对象shape-area(); // 调用圆形类的 area 函数return 0;
}2.2 纯虚函数
纯虚函数是指在基类中定义一个虚函数但不为它提供具体实现而要求子类必须重写这个函数。在定义纯虚函数时需要在函数声明中加上 0。
下面是一个纯虚函数的例子定义了一个 Shape 基类并在其中定义了一个纯虚函数 print。
#include iostream
using namespace std;class Shape {public:// 声明纯虚函数 printvirtual void print() 0;
};// Circle 子类继承 Shape 基类
class Circle: public Shape {public:// 重写Shape类中的 print 函数void print() {cout 这是一个圆形 endl;}
};// Rectangle 子类继承 Shape 基类
class Rectangle: public Shape {public:// 重写Shape类中的 print 函数void print() {cout 这是一个矩形 endl;}
};int main() {Circle cir; // 声明圆形对象Rectangle rec; // 声明矩形对象cir.print(); // 调用 Circle 类的 print 函数rec.print(); // 调用 Rectangle 类的 print 函数return 0;
}以上就是C中实现多态的两种方式分别是静态多态和动态多态。无论是哪种方式都可以让我们的代码更加灵活易于维护和扩展提高开发效率。
三、虚函数
接上文继续来看虚函数它允许在基类中定义一个函数而在派生类中对该函数进行重写。下面介绍虚函数的定义、实现方式和需要注意的事项。
1. 定义
虚函数是在基类中通过 virtual 关键字声明的函数它可以被子类重写实现不同行为。虚函数的作用是允许派生类根据自身的需要对基类中的函数进行扩展以实现多态性。
class BaseClass {
public:virtual void virtual_fun() {std::cout BaseClass 的虚函数 std::endl;}
};class DerivedClass : public BaseClass {
public:void virtual_fun() {std::cout DerivedClass 的虚函数 std::endl;}
};在代码中先声明了一个基类 BaseClass其中有一个名为 virtual_fun 的虚函数。定义了一个派生类 DerivedClass也有一个同名的虚函数 virtual_fun但实现方式不同。这样就可以直接在派生类中对基类的虚函数进行重写随时根据需要进行调用。
2. 实现
使用虚函数时可以通过基类的指针或者引用调用实际派生类中的函数版本实现不同的行为。基类指针或引用调用虚函数时会根据实际对象类型决定调用的函数版本这被称为动态绑定dynamic binding。使用动态绑定可以实现 C 中的多态性。
BaseClass *base_pointer;
DerivedClass derived_object;
base_pointer derived_object;
base_pointer-virtual_fun();在这段代码中定义了一个基类指针 base_pointer指向一个派生类对象 derived_object。接着通过指针调用了派生类中的虚函数 virtual_fun。由于 base_pointer 实际指向的是派生类对象在运行时会根据实际对象类型执行派生类中的函数版本输出 “DerivedClass 的虚函数”。
3. 注意
子类重写虚函数时需要保证函数签名一致性。这包括参数列表、返回类型和函数名必须与基类中的虚函数一致。如果派生类中的函数与基类中的虚函数签名不一致将无法实现动态绑定。
此外还要注意虚函数的效率问题。虚函数的调用需要在运行时动态确认函数版本因此它的效率可能会较低。如果需要进行高性能的程序开发应该尽可能减少虚函数的使用。
四、纯虚函数
继续来看纯虚函数它是基类中通过virtual和 0关键字声明的函数没有具体的实现。必须由子类实现之后才能使用。
1. 定义
定义纯虚函数需要在基类中使用virtual和 0关键字如下所示
class BaseClass {
public:virtual void pure_virtual_func() 0; // 声明纯虚函数
};class DerivedClass : public BaseClass {
public:void pure_virtual_func() override {// 实现基类纯虚函数}
};在代码中在 BaseClass 中声明了一个纯虚函数 pure_virtual_func并且在 DerivedClass 中重写了该函数。
2. 作用
纯虚函数广泛应用于接口编程。它定义了一组必须要实现的接口由不同的派生类来实现具体的行为逻辑以达到最大的代码重用。
同时纯虚函数也可作为基类中的抽象方法在基类中被调用以保证派生类在调用的时候已经实现了该纯虚函数。
class BaseClass {
public:virtual void pure_virtual_func() 0; // 声明纯虚函数void another_func() {// 使用纯虚函数pure_virtual_func();}
};class DerivedClass : public BaseClass {
public:void pure_virtual_func() override {// 实现基类纯虚函数}
};在代码中在 BaseClass 中声明了一个普通函数 another_func其中调用了纯虚函数 pure_virtual_func。在派生类中实现 pure_virtual_func 后another_func 就可以被正常的调用。
五、虚析构函数
虚析构函数是通过 virtual 关键字在基类中声明的析构函数。虚析构函数的作用是在释放派生类时先调用派生类的析构函数再调用基类的析构函数保证程序的正常运行。
1. 定义
可以通过在基类的析构函数前加上 virtual 关键字来定义虚析构函数如下所示
class BaseClass {
public:virtual ~BaseClass() {// 基类析构函数};
};class DerivedClass : public BaseClass {
public:~DerivedClass() override {// 派生类析构函数};
};在代码中通过 virtual 关键字在基类中定义了虚析构函数 ~BaseClass()。在派生类 DerivedClass 中我们重写了析构函数 ~DerivedClass()保证在释放 DerivedClass 对象时先调用派生类的析构函数再调用基类的析构函数。
2. 作用
虚析构函数的作用是在释放派生类时先调用派生类的析构函数再调用基类的析构函数避免程序崩溃。
如果不使用虚析构函数当我们在基类中没有定义虚析构函数再释放派生类对象时只会调用基类的析构函数不会调用派生类的析构函数这可能会导致一些意外错误的发生。
代码示例
#include iostreamclass BaseClass {
public:~BaseClass() {std::cout In BaseClass Destructor std::endl;}
};class DerivedClass : public BaseClass {
public:~DerivedClass() {std::cout In DerivedClass Destructor std::endl;}
};int main() {BaseClass *ptr new DerivedClass();delete ptr;return 0;
}在代码中声明了一个指向 DerivedClass 对象的基类指针 ptr在释放 ptr 所指向的对象时只会调用基类 BaseClass 的析构函数而不会调用派生类 DerivedClass 的析构函数。这可能会导致一些内存资源泄漏和程序错误的发生。所以使用虚析构函数也是保证程序正常运行的一种良好的编程习惯。
六、 抽象类
C抽象类是一种特殊的类拥有至少一个纯虚函数的类。抽象类不能被实例化只能用作接口并让子类去实现。接下来将更加详细地讨论抽象类。
抽象类是一个包含了至少一个纯虚函数的类。纯虚函数是一个在类中定义但没有实现的虚函数仅用于设定接口规格。换句话说抽象类是一个不能被实例化的基类只有当它的派生类实现了所规定的接口时程序才能正常运行。
以下是一个简单的抽象类示例代码
#include iostream// 抽象类
class Shape {
public:// 纯虚函数virtual double getArea() const 0;// 普通函数void printName() {std::cout Shape std::endl;}
};// 派生类
class Rectangle : public Shape {
public:// 构造函数Rectangle(double width, double height) : m_width(width), m_height(height) {}// 重写基类中的纯虚函数double getArea() const override {return m_width * m_height;}private:double m_width;double m_height;
};// 主函数
int main() {// 使用指向基类对象的指针Shape *shape new Rectangle(3, 4);// 调用基类的普通函数shape-printName();// 调用派生类中重写的纯虚函数std::cout The area of the rectangle is: shape-getArea() std::endl;// 释放动态分配的内存delete shape;return 0;
}在代码中定义了一个抽象基类 Shape包含一个纯虚函数 getArea() 和一个普通函数 printName() 定义一个派生类 Rectangle重写了基类中的纯虚函数并通过构造函数初始化了 m_width 和 m_height 在 main() 函数中使用基类指针创建了一个派生类对象并调用了基类的普通函数和派生类的重写函数实现了多态的效果。最终释放了动态分配的内存。
七、实现多态的注意事项
在C 中实现多态很常见这也是 OOP面向对象编程的核心之一。但是在实现多态时往往需要我们注意一些事项。接下来我们将详细介绍这些事项
1. 基类虚函数必须使用 virtual 关键字声明
想要实现多态必须使用虚函数。同时在定义基类中的虚函数时必须使用 virtual 关键字声明
class Shape {
public:virtual double getArea() const {return 0.0;}
};2. 子类重写基类的虚函数时必须保持函数签名的一致性
当子类需要重写基类的虚函数时必须保持函数签名的一致性。也就是说参数列表和返回类型必须与基类中的虚函数一致。
例如有一个派生类 Rectangle通过重写基类中的虚函数 getArea() 来计算矩形的面积
class Rectangle : public Shape {
public:double getArea() const override {return m_width * m_height;}private:double m_width;double m_height;
};需要注意的是使用了 override 关键字来显式地告知编译器我们正在重写基类中的虚函数
3. 在析构函数中使用 virtual 关键字声明
在使用多态时如果用基类指针指向派生类对象并在析构时忘记使用 virtual 关键字则可能导致派生类对象的内存得不到释放最终导致内存泄漏和崩溃等问题
class Shape {
public:virtual double getArea() const {return 0.0;}virtual ~Shape() {}
};需要注意在虚析构函数中添加了 virtual 关键字。这样在我们释放一个使用基类指针指向的对象时将会自动调用派生类的析构函数即使它是在基类中定义的
4. 在使用虚继承时需要注意继承的正确性
虚继承virtual inheritance是一种用于处理多重继承的机制可以避免引入二义性。在使用虚继承时需要注意内存对齐和字节对齐等问题以保证继承的正确性
class Shape {
public:virtual double getArea() const 0;
};class Solid : virtual public Shape {
public:virtual double getVolume() const 0;
};需要注意的是使用了 virtual 关键字来声明虚继承。这里Solid 类继承了 Shape 类同时也成为了一个抽象类
5. 在使用抽象类时需要让子类去实现纯虚函数
抽象类是指具有纯虚函数的类不能被实例化。如果一个派生类没有实现从基类继承而来的纯虚函数则该派生类也将成为一个抽象类。
class Shape {
public:virtual double getArea() const 0;
};这样我们就将 Shape 类变成了一个抽象类。从这个抽象类中派生出来的类必须实现 getArea() 函数否则它们也将成为抽象类。
八、 小结
继承的概念讲到这里之后在项目中的实际体现还需要大家在实际项目中不断总结积累加油