用c实现C++类(八股)

在 C 语言中,虽然没有内建的面向对象编程(OOP)特性(如封装、继承、多态),但通过一些编程技巧,我们仍然可以模拟实现这些概念。下面将用通俗易懂的方式,逐步介绍如何在 C 中实现封装、继承和多态。

1. 封装(Encapsulation)

封装是指将数据和操作数据的函数绑定在一起,隐藏内部实现细节,只暴露必要的接口。在 C 中,我们可以通过 struct 和相关的函数来实现封装。

假设我们要创建一个“矩形”对象,包含宽度和高度,并提供计算面积的功能。

步骤:

  1. 定义结构体(隐藏内部细节)
  2. 提供创建和操作该结构体的函数

实现:

// Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_Htypedef struct Rectangle Rectangle;// 创建矩形
Rectangle* Rectangle_create(double width, double height);// 销毁矩形
void Rectangle_destroy(Rectangle* rect);// 计算面积
double Rectangle_getArea(const Rectangle* rect);#endif // RECTANGLE_H// Rectangle.c
#include <stdlib.h>
#include "Rectangle.h"// 定义结构体(隐藏在 .c 文件中)
struct Rectangle {double width;double height;
};// 创建矩形
Rectangle* Rectangle_create(double width, double height) {Rectangle* rect = (Rectangle*)malloc(sizeof(Rectangle));if (rect != NULL) {rect->width = width;rect->height = height;}return rect;
}// 销毁矩形
void Rectangle_destroy(Rectangle* rect) {free(rect);
}// 计算面积
double Rectangle_getArea(const Rectangle* rect) {if (rect == NULL) return 0.0;return rect->width * rect->height;
}// main.c
#include <stdio.h>
#include "Rectangle.h"int main() {Rectangle* rect = Rectangle_create(5.0, 3.0);if (rect != NULL) {printf("面积: %.2f\n", Rectangle_getArea(rect));Rectangle_destroy(rect);}return 0;
}

说明:

  • 隐藏实现Rectangle 的具体结构体定义在 Rectangle.c 中,外部无法直接访问其成员变量。
  • 接口函数:通过 Rectangle_createRectangle_destroyRectangle_getArea 提供对 Rectangle 对象的操作。

2. 继承(Inheritance)

继承允许一个“子类”拥有“父类”的属性和行为。在 C 中,我们可以通过在子结构体中包含父结构体来模拟继承。

基类“形状”和子类“矩形”和“圆形”

步骤:

  1. 定义基类结构体,包含一个指向函数的指针(模拟虚函数表)。
  2. 定义子类结构体,在其中包含基类结构体。
  3. 实现子类的功能

实现:

// Shape.h
#ifndef SHAPE_H
#define SHAPE_Htypedef struct Shape Shape;// 虚函数表
typedef struct {double (*getArea)(const Shape* self);void (*destroy)(Shape* self);
} ShapeVTable;// 基类结构体
struct Shape {ShapeVTable* vtable;
};// 基类接口函数
double Shape_getArea(const Shape* self);
void Shape_destroy(Shape* self);#endif // SHAPE_H// Shape.c
#include "Shape.h"double Shape_getArea(const Shape* self) {if (self && self->vtable && self->vtable->getArea) {return self->vtable->getArea(self);}return 0.0;
}void Shape_destroy(Shape* self) {if (self && self->vtable && self->vtable->destroy) {self->vtable->destroy(self);}
}// Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H#include "Shape.h"typedef struct {Shape base; // 继承自 Shapedouble width;double height;
} Rectangle;// 创建矩形
Shape* Rectangle_create(double width, double height);#endif // RECTANGLE_H// Rectangle.c
#include <stdlib.h>
#include "Rectangle.h"// 矩形的虚函数实现
double Rectangle_getArea(const Shape* self) {const Rectangle* rect = (const Rectangle*)self;return rect->width * rect->height;
}void Rectangle_destroy_impl(Shape* self) {free(self);
}// 定义矩形的虚函数表
ShapeVTable rectangle_vtable = {.getArea = Rectangle_getArea,.destroy = Rectangle_destroy_impl
};// 创建矩形
Shape* Rectangle_create(double width, double height) {Rectangle* rect = (Rectangle*)malloc(sizeof(Rectangle));if (rect != NULL) {rect->base.vtable = &rectangle_vtable;rect->width = width;rect->height = height;}return (Shape*)rect;
}// Circle.h
#ifndef CIRCLE_H
#define CIRCLE_H#include "Shape.h"typedef struct {Shape base; // 继承自 Shapedouble radius;
} Circle;// 创建圆形
Shape* Circle_create(double radius);#endif // CIRCLE_H// Circle.c
#include <stdlib.h>
#include "Circle.h"
#include <math.h>// 圆形的虚函数实现
double Circle_getArea(const Shape* self) {const Circle* circle = (const Circle*)self;return M_PI * circle->radius * circle->radius;
}void Circle_destroy_impl(Shape* self) {free(self);
}// 定义圆形的虚函数表
ShapeVTable circle_vtable = {.getArea = Circle_getArea,.destroy = Circle_destroy_impl
};// 创建圆形
Shape* Circle_create(double radius) {Circle* circle = (Circle*)malloc(sizeof(Circle));if (circle != NULL) {circle->base.vtable = &circle_vtable;circle->radius = radius;}return (Shape*)circle;
}// main.c
#include <stdio.h>
#include "Shape.h"
#include "Rectangle.h"
#include "Circle.h"int main() {Shape* shapes[2];shapes[0] = Rectangle_create(5.0, 3.0); // 创建矩形shapes[1] = Circle_create(2.0);         // 创建圆形for (int i = 0; i < 2; ++i) {printf("图形 %d 的面积: %.2f\n", i + 1, Shape_getArea(shapes[i]));Shape_destroy(shapes[i]);}return 0;
}

说明:

  • 基类 Shape:包含一个虚函数表 vtable,用于指向具体实现的函数。
  • 子类 RectangleCircle
    • 包含 Shape 作为第一个成员,实现“继承”。
    • 定义自己的虚函数(如 getAreadestroy_impl)。
    • 分别创建自己的虚函数表,并在创建时将 vtable 指向自己的表。
  • 多态:在 main 中,通过基类指针 Shape* 调用 getArea,根据实际对象类型(矩形或圆形)执行不同的函数。

3. 多态(Polymorphism)

多态允许不同类型的对象通过相同的接口调用不同的实现。在上面的继承示例中,我们已经部分实现了多态。下面进一步解释多态的实现。

Shape 基类中定义了虚函数表 ShapeVTable,包含 getAreadestroy 函数指针。每个子类(如 RectangleCircle)都提供了自己的实现,并在创建时将 vtable 指向自己的函数表。

如何工作:

  1. 统一接口:所有形状都通过 Shape* 指针进行操作。
  2. 具体实现:不同的形状(矩形、圆形)有各自的 getArea 实现。
  3. 调用时自动选择:根据对象的实际类型,调用相应的 getArea 函数。

示例解释:

for (int i = 0; i < 2; ++i) {printf("图形 %d 的面积: %.2f\n", i + 1, Shape_getArea(shapes[i]));Shape_destroy(shapes[i]);
}
  • Shape_getArea(shapes[i]) 会根据 shapes[i]vtable 指向不同的 getArea 实现,自动计算出矩形或圆形的面积。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/505793.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

H2数据库在单元测试中的应用

H2数据库特征 用比较简洁的话来介绍h2数据库&#xff0c;就是一款轻量级的内存数据库&#xff0c;支持标准的SQL语法和JDBC API&#xff0c;工业领域中&#xff0c;一般会使用h2来进行单元测试。 这里贴一下h2数据库的主要特征 Very fast database engineOpen sourceWritten…

R语言在森林生态研究中的魔法:结构、功能与稳定性分析——发现数据背后的生态故事!

森林生态系统结构、功能与稳定性分析与可视化研究具有多方面的重要意义&#xff0c;具体如下&#xff1a; 一、理论意义 ●深化生态学理论 通过研究森林生态系统的结构、功能与稳定性&#xff0c;可以深化对生态系统基本理论的理解。例如&#xff0c;生物多样性与生态系统稳定性…

【C++经典例题】求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a; 期待您的关注 题目描述&#xff1a; 原题链接&#xff1a; 求123...n_牛客题霸_牛客网 (nowcoder.com) 解题思路&#xff1a; …

day01-HTML-CSS——基础标签样式表格标签表单标签

目录 此篇为简写笔记下端1-3为之前笔记&#xff08;强迫症、保证文章连续性&#xff09;完整版笔记代码模仿新浪新闻首页完成审核不通过发不出去HTMLCSS1 HTML1.1 介绍1.1.1 WebStrom中基本配置 1.2 快速入门1.3 基础标签1.3.1 标题标签1.3.2 hr标签1.3.3 字体标签1.3.4 换行标…

基于Springboot+Vue的仓库管理系统

开发一个基于Spring Boot和Vue的仓库管理系统涉及到前端和后端的开发。本文呢&#xff0c;给出一个简单的开发步骤指南&#xff0c;用于指导初入的新手小白如何开始构建这样一个系统&#xff0c;如果**你想直接学习全部内容&#xff0c;可以直接拉到文末哦。** 开始之前呢给小…

快速导入请求到postman

1.确定请求&#xff0c;右键复制为cURL(bash) 2.postman菜单栏Import-Raw text&#xff0c;粘贴复制的内容保存&#xff0c;请求添加成功

预训练语言模型——BERT

1.预训练思想 有了预训练就相当于模型在培养大学生做任务&#xff0c;不然模型初始化再做任务就像培养小学生 当前数据层面的瓶颈是能用于预训练的语料快被用完了 现在有一个重要方向是让机器自己来生成数据并做微调 1.1 预训练&#xff08;Pre - training&#xff09;vs. 传…

数字孪生电网有什么作用?实时云渲染技术又如何赋能智慧电网?

电网系统的结构比较复杂&#xff0c;传统运维模式主要是依赖传感器和人工巡检&#xff0c;难以全面监测管理。而数字孪生技术的应用将推动电网智能化、绿色化的高效转型。 智慧电网利用物理模型、现场测量数据和历史数据&#xff0c;结合云计算、物联网、大数据等技术&#xf…

MiniMind - 从0训练语言模型

文章目录 一、关于 MiniMind &#x1f4cc;项目包含 二、&#x1f4cc; Environment三、&#x1f4cc; Quick Start Test四、&#x1f4cc; Quick Start Train0、克隆项目代码1、环境安装2、如果你需要自己训练3、测试模型推理效果 五、&#x1f4cc; Data sources1、分词器&am…

EasyCVR视频汇聚平台如何配置webrtc播放地址?

EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。平台支持多协议接入&#xff0c;能将接入到视频流转码为多格式进行分发&#xff0c;包括RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、W…

【GlobalMapper精品教程】093:将tif影像色彩映射表(调色板)转为RGB全彩模式

参考阅读:【ArcGIS微课1000例】0137:色彩映射表转为RGB全彩模式 文章目录 一、Globalmapper中显示模式二、ArcGIS中显示模式三、调色板转为RGB全彩模式四、注意事项一、Globalmapper中显示模式 Globalmapper中,将谷歌等多种来源在线影像下载到本地后,可能会遇到以下数据格…

Postman接口测试05|实战项目笔记

目录 一、项目接口概况 二、单接口测试-登录接口&#xff1a;POST 1、正例 2、反例 ①姓名未注册 ②密码错误 ③姓名为空 ④多参 ⑤少参 ⑥无参 三、批量运行测试用例 四、生成测试报告 1、Postman界面生成 2、Newman命令行生成 五、token鉴权&#xff08;“…

【css】浏览器强制设置元素状态(hover|focus……)

直接上步骤&#xff1a; 打开浏览器控制台 → 找到样式选项 → 找到:hov选项 → 点击:hov选项&#xff0c;会展开【设置元素状态】。 只要选中就会展示出自己写在css里面的该种状态下的样式了。

Springboot——钉钉(站内)实现登录第三方应用

文章目录 前言准备1、创建钉钉应用&#xff0c;并开放网页应用2、配置网页应用各项参数发布版本 前端改造后端逻辑1、获取应用免登录 Access_token2、通过免登录 Access_token 和 Auth_Code 获取对应登录人信息 注意事项 前言 PC端的钉钉中工作台&#xff0c;增加第三方应用&a…

完美解决VMware 17.0 Pro安装ubuntu、Deepin等虚拟机后卡顿、卡死问题

这两天在 VM 17 Pro 中安装了ubuntu 24.1 和Deepin 23.9 等Linux操作系统&#xff0c;在使用过程中出现过数次卡顿、卡死问题&#xff0c;现记录整理解决方法如下&#xff1a; 一、问题描述 安装虚拟机时、以及安装完成后正常使用时出现鼠标点击卡顿、系统反应慢、卡死等问题…

计算机的错误计算(二百零七)

摘要 利用两个数学大模型计算 arccot(0.125664e2)的值&#xff0c;结果保留16位有效数字。 实验表明&#xff0c;它们的输出中分别仅含有3位和1位正确数字。 例1. 计算 arccot(0.125664e2)的值&#xff0c;结果保留16位有效数字。 下面是与一个数学解题器的对话。 以上为与…

Linux内核TTY子系统有什么(6)

接前一篇文章&#xff1a;Linux内核TTY子系统有什么&#xff08;5&#xff09; 本文内容参考&#xff1a; Linux TTY子系统框架-CSDN博客 一文彻底讲清Linux tty子系统架构及编程实例-CSDN博客 linux TTY子系统(3) - tty driver_sys tty device driver-CSDN博客 Linux TTY …

03_Redis基本操作

1.Redis查询命令 1.1 官网命查询命令 为了便于学习Redis,官方将其用于操作不同数据类型的命令进行了分类整理。你可以通过访问Redis官方网站上的命令参考页面https://redis.io/commands来查阅这些分组的命令,这有助于更系统地理解和使用Redis的各项功能。 1.2 HELP查询命令…

@LocalBuilder装饰器: 维持组件父子关系

一、前言 当开发者使用Builder做引用数据传递时&#xff0c;会考虑组件的父子关系&#xff0c;使用了bind(this)之后&#xff0c;组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致的问题&#xff0c;引入LocalBuilder装饰器。…

kubernetes第七天

1.影响pod调度的因素 nodeName 节点名 resources 资源限制 hostNetwork 宿主机网络 污点 污点容忍 Pod亲和性 Pod反亲和性 节点亲和性 2.污点 通常是作用于worker节点上&#xff0c;其可以影响pod的调度 语法&#xff1a;key[value]:effect effect:[ɪˈfek…