C语言模拟“类”的实现

关于C语言模拟“类”的实现——面向对象编程

功能分析:计算一个图形的面积

实现分析:

  1. 要计算一个图形的面积,需要有该图形特定的参数,例如矩形需要宽度和高度,圆需要半径,所以需要将这些参数和对应的图形名称一一对应,并封装起来
  2. 一个封闭的常见图形都可以使用初等数学知识计算其面积,这个图形可以是圆形,也可以是矩形等,可计算面积算是封闭图形的共有的属性,所以可以将这些共性继承给各图形
  3. 一个图形可以是圆形,可以是矩形等,要为了针对不同的图形都能计算其面积,计算面积的函数应该针对不同的图形需要有多种运行状态,简称多态

封装、继承、多态的实现

封装是指将数据方法绑定在一起,并隐藏内部实现细节,在 C 语言中,可以通过 结构体函数 来模拟实现封装。通常结构体类型的定义放置于头文件(.h文件),函数的定义放置于源文件(.c文件),函数的声明放置于头文件(.h文件)。

可计算面积算是封闭图形的共有的方法(函数),根据共同属性,定义如下类似父类的结构体类型进行封装:

Shape.h文件

//父类
typedef struct
{
    double (*Area)(const void* self);  // 函数指针,模拟虚函数
} Shape;

对于不同属性,分别衍生出类似子类,例如圆形和矩形:

Shape
Circle
Rectangle
...

这里使用嵌套结构体进行继承父类的属性:

Shape.h文件

//子类 1:圆形
typedef struct
{
    Shape base;  // 继承父类
    double radius;  // 半径
} Circle;

Shape.h文件

//子类 2:矩形
typedef struct
{
    Shape base;  // 继承父类
    double width, height;  // 宽度和高度
} Rectangle;

上述定义中可以看到,子类继承了父类的共有方法,即计算图形的面积。

下面为了实现多态功能(不同的图形使用不同的计算面积公式),需要在源文件中根据图形的类型,定义不同的计算面积的函数。

Shape.c文件

double Circle_Area(const void* self) 
{
    const Circle* obj = (const Circle*)self;
    return 3.14159 * obj->radius * obj->radius;  // 面积公式:πr²
}
double Rectangle_Area(const void* self)
{
    const Rectangle* obj = (const Rectangle*)self;
    return obj->width * obj->height;  // 面积公式:width * height
}

这里可以看到,形参self使用的是const void* 类型,是一种特殊的指针类型,它用于指向任何类型的数据,可以使用这个指针获取指向的地址中的数据,但是不能通过这个指针来修改数据的内容。

下面便是在主函数main中先定义两个形状,分别是半径为10的圆形和宽是3.14、高是2.71的矩形。定义完成后,为了实现多态功能,使用父类指针指向子类对象,并只需要调用父类中的共有方法——计算面积,便可实现两种图形的面积计算。

main.c文件

#include <stdio.h>

int main()
{
    Circle shape_1 = { { Circle_Area }, 10 };//定义一个圆形的图形对象并初始化参数
	Rectangle shape_2 = { { Rectangle_Area }, 3.14, 2.71 };//定义一个矩形的图形对象并初始化参数
    
    Shape* shapes[] = { (Shape*)&shape_1, (Shape*)&shape_2 };  // 多态:父类指针指向子类对象
    for (int i = 0; i < 2; i++)
    {
        printf("Shape_%d's Area: %.2f\n", i + 1, shapes[i]->Area(shapes[i]));//只需要调用一个方法,便可完成不同形状的面积计算
    }
    
    return 0;
}

输出结果为:

Shape_1's Area: 314.16
Shape_2's Area: 8.51

程序整合

为了方便验证,下面直接给出单文件程序:

#include <stdio.h>

//父类
typedef struct
{
    double (*Area)(const void* self);  // 函数指针,模拟虚函数
} Shape;

//子类 1:圆形
typedef struct
{
    Shape base;  // 继承父类
    double radius;  // 半径
} Circle;

//子类 2:矩形
typedef struct
{
    Shape base;  // 继承父类
    double width, height;  // 宽度和高度
} Rectangle;

double Circle_Area(const void* self) 
{
    const Circle* obj = (const Circle*)self;
    return 3.14159 * obj->radius * obj->radius;  // 面积公式:πr²
}

double Rectangle_Area(const void* self)
{
    const Rectangle* obj = (const Rectangle*)self;
    return obj->width * obj->height;  // 面积公式:width * height
}

int main()
{
    Circle shape_1 = { { Circle_Area }, 10 };//定义一个圆形的图形对象并初始化参数
	Rectangle shape_2 = { { Rectangle_Area }, 3.14, 2.71 };//定义一个矩形的图形对象并初始化参数
    
    Shape* shapes[] = { (Shape*)&shape_1, (Shape*)&shape_2 };  // 多态:父类指针指向子类对象
    for (int i = 0; i < 2; i++)
    {
        printf("Shape_%d's Area: %.2f\n", i + 1, shapes[i]->Area(shapes[i]));//只需要调用一个方法,便可完成不同形状的面积计算
    }
    
    return 0;
}

使用C++实现

由于C++是支持面向对象编程的,故可以直接定义类如下:

#ifndef _SHAPE_H
#define _SHAPE_H

// 父类
class Shape {
public:
    virtual double Area() const = 0; // virtual表示虚函数,=0表示纯虚函数,子类必须提供实现
    virtual ~Shape() = default;      // 虚析构函数,确保正确释放子类对象
};

#endif // SHAPE_H
#ifndef _SHAPE_H
#define _SHAPE_H

// 父类
class Shape {
public:
    virtual double Area() const = 0; // 纯虚函数,子类必须实现
    virtual ~Shape() = default;      // 虚析构函数,确保正确释放子类对象
};

// 子类 1:圆形
class Circle : public Shape {
private:
    double radius; // 半径
public:
    Circle(double r); // 构造函数
    double Area() const override; // 重写父类的虚函数
};

// 子类 2:矩形
class Rectangle : public Shape {
private:
    double width, height; // 宽度和高度
public:
    Rectangle(double w, double h); // 构造函数
    double Area() const override; // 重写父类的虚函数
};

#endif

使用Matlab实现

由于matlab是支持面向对象编程的,故可以直接定义类如下:

Shape.m文件

% 父类
classdef Shape
    methods (Abstract)
        Area(self)
    end
end

Circle.m文件

% 子类 1:圆形
classdef Circle < Shape
    properties
        radius
    end
    
    methods
        function obj = Circle(radius)
            obj.radius = radius;
        end
        
        function area = Area(obj)
            area = pi * obj.radius^2;
        end
    end
end

Rectangle.m文件

% 子类 2:矩形
classdef Rectangle < Shape
    properties
        width
        height
    end
    
    methods
        function obj = Rectangle(width, height)
            obj.width = width;
            obj.height = height;
        end
        
        function area = Area(obj)
            area = obj.width * obj.height;
        end
    end
end

注意每个类都单独放在一个m文件,下面的命令可以在脚本文件中运行,也可以直接在命令行窗口中输入运行:

% 主程序
shape_1 = Circle(10);  % 定义一个圆形的图形对象并初始化参数
shape_2 = Rectangle(3.14, 2.71);  % 定义一个矩形的图形对象并初始化参数

disp(['Shape_1''s Area:' , num2str(shape_1.Area)]);
disp(['Shape_2''s Area:' , num2str(shape_2.Area)]);

使用pyhton实现

可以使用ABC抽象基类实现多态:

from abc import ABC, abstractmethod
#ABC:抽象基类(Abstract Base Class),abstractmethod是标准库abc模块中定义的一个特定装饰器

# 抽象父类
class Shape(ABC):	#继承自ABC
    @abstractmethod	#装饰下面的方法,要求方法必须在子类中实现
    def Area(self):
        pass  

或者使用定义抽象方法,强制子类必须实现某个方法,否则会抛出异常

# 父类
class Shape:
    def area(self):
        raise NotImplementedError("Subclasses must implement this method")
        #NotImplementedError 是 Python 中的一个内置异常类,通常用于表示某个方法或功能尚未实现。当定义一个父类,并希望子类必须实现某个方法时,可以在父类中使用 raise

下面便是各个子类:

# 子类 1:圆形
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius  # 半径

    def Area(self):
        return math.pi * self.radius ** 2  # 面积公式:πr²
# 子类 2:矩形
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width   # 宽度
        self.height = height # 高度

    def Area(self):
        return self.width * self.height  # 面积公式:width * height

整合后如下:

Shape.py文件

import math
from abc import ABC, abstractmethod

class Shape:
    def area(self):
        raise NotImplementedError("Subclasses must implement this method")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

shape_1 = Circle(10)
shape_2 = Rectangle(3.14, 2.71)

print(f"Shape_1's Area: {shape_1.area()}")
print(f"Shape_2's Area: {shape_2.area()}")

输出结果为:

Shape_1's Area: 314.16
Shape_2's Area: 8.51

下面给出一个通过父类的方法统一调用子类的实现,这种方式可以隐藏具体的子类细节,使得代码更加通用和简洁,但是AI提示代码会有些冗余,Shape类的 area 方法并没有实际功能,只是将调用转发给子类的方法。

import math

class Shape:
    def area(self, shape):
        return shape.area(self)

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self, shape):
        return math.pi * self.radius ** 2  # 面积公式:πr²
    
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width   # 宽
        self.height = height # 高度

    def area(self, shape):
        return self.width * self.height  # 面积公式:width * height
    
shape = Shape()
shape_1 = Circle(10)           # 定义一个圆形的图形对象并初始化参数
shape_2 = Rectangle(3.14, 2.71) # 定义一个矩形的图形对象并初始化参数

print(f"Shape_1's Area: {shape.area(shape_1)}")
print(f"Shape_2's Area: {shape.area(shape_2)}")

优点如下:

  1. 统一接口:所有子类的 area 方法都通过父类的 area 方法调用,接口统一。
  2. 隐藏实现细节:调用者不需要知道具体的子类类型,只需要知道父类 Shape 即可。

总结

可以发现,相比于C语言模拟类的方法而言,直接面向对象编程的语言可以直接定义类,并且实现封装、继承、多态的特性,语法更加简洁,便于理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值