C 语言中的多态性:结构体与函数指针的巧妙结合

相关文章系列

如何编写可读性高的 C 代码?-CSDN博客

目录

1.C的多态实现

2.实现多态的代码需要注意以下几点


1.C的多态实现

        多态性是面向对象编程中的一个重要概念,它允许不同类型的对象对相同的消息做出不同的响应。在 C 语言中,我们虽然没有类和对象的概念,但我们仍然可以通过结构体和函数指针来模拟多态性的行为。具体来说,我们可以通过定义一个包含函数指针的结构体,并在派生(子类)结构体中包含基类(父类)结构体的方式来实现多态性。

        我们知道结构体的内存布局是连续的,结构体的成员按照定义的顺序依次存放在内存中。这为多态性的实现提供了基础,因为我们可以在派生类中包含基类的结构体,从而保证派生类对象的内存布局与基类对象兼容。

        在 C 语言中,指针的大小与机器的字长有关,通常为4字节或8字节。通过使用基类指针指向派生类对象,并通过该指针调用基类的虚函数,我们可以实现多态性。这是因为指针的大小固定,编译器能够准确地计算偏移量并调用正确的函数实现。

#include <stdio.h>// 声明基类
struct Animal {void (*eat)(struct Animal *animal);
};// 定义基类方法
void Animal_eat(struct Animal *animal) {printf("Animal is eating\n");
}// 声明派生类
struct Dog {struct Animal super;  // 派生类中包含基类的结构体int age;
};// 定义派生类的eat方法
void Dog_eat(struct Animal *animal) {struct Dog *dog = (struct Dog *)animal;  // 将基类指针转换为派生类指针printf("Dog is eating, age: %d\n", dog->age);
}int main() {// 创建一个派生类对象struct Dog myDog;myDog.super.eat = Dog_eat;  // 将基类的函数指针指向派生类的方法myDog.age = 3;// 调用eat方法,实现多态myDog.super.eat((struct Animal *)&myDog);  // 通过基类的指针调用虚函数return 0;
}

在这个示例中,我们定义了一个基类 Animal 和一个派生类 Dog,并实现了多态性的行为。当然,让我们进一步解释代码的每个部分:

1) 基类 Animal 的声明

struct Animal {void (*eat)(struct Animal *animal);
};

这里我们定义了一个基类 Animal,它包含了一个函数指针 eat,用于表示动物吃东西的行为。

2) 基类方法 Animal_eat 的定义

void Animal_eat(struct Animal *animal) {printf("Animal is eating\n");
}

这个函数实现了动物吃东西的行为,输出一条消息表示动物正在进食。

3) 派生类 Dog 的声明

struct Dog {struct Animal super;  // 派生类中包含基类的结构体int age;
};

在这里,我们定义了一个派生类 Dog,它包含了基类 Animal 的结构体作为其成员,并额外添加了一个表示年龄的整数。

4) 派生类方法 Dog_eat 的定义

void Dog_eat(struct Animal *animal) {struct Dog *dog = (struct Dog *)animal;  // 将基类指针转换为派生类指针printf("Dog is eating, age: %d\n", dog->age);
}

这个函数实现了狗吃东西的行为,首先将基类指针转换为派生类指针,然后输出一条消息表示狗正在进食,并显示狗的年龄。

5) main 函数中的对象创建和方法调用

struct Dog myDog;
myDog.super.eat = Dog_eat;  // 将基类的函数指针指向派生类的方法
myDog.age = 3;

在 main 函数中,我们创建了一个 Dog 类型的对象 myDog,并将基类的函数指针 eat 指向派生类的方法 Dog_eat。然后设置了狗的年龄为 3。

myDog.super.eat((struct Animal *)&myDog);

这一行代码调用了 myDog 对象的 eat 方法,实现了多态性。通过将基类指针传递给派生类方法,我们实现了动态绑定,即根据实际对象的类型来选择正确的方法实现。

2.实现多态的代码需要注意以下几点

1) 指针转换的安全性:在派生类方法 Dog_eat 中,将基类指针转换为派生类指针:struct Dog *dog = (struct Dog *)animal;。这种转换在一定程度上是危险的,因为如果 animal 指针实际指向的不是 Dog 类型的对象,而是其他类型的对象,那么转换就会出错。因此,在实际应用中,我们应该确保基类指针指向的是正确的对象类型,或者通过其他手段来确保转换的安全性,比如使用类型检查或者设计更加健壮的数据结构。

C++之数据转换(全)_c++数据类型转换-CSDN博客

2) 函数指针的赋值:在 main 函数中,通过将基类的函数指针 eat 指向派生类的方法 Dog_eat,实现了动态绑定和多态性。但是需要确保函数签名匹配,即派生类方法的签名必须与基类方法的签名完全一致,否则会导致编译错误或者运行时错误。

3) 内存布局的一致性:在派生类中包含基类的结构体成员是实现多态性的关键之一。需要确保派生类对象的内存布局与基类对象兼容(即基类对象需要在派生类对象属性中的第一个位置),以便可以安全地将基类指针指向派生类对象,并通过该指针调用基类的虚函数。

4) 函数指针调用的正确性:在 main 函数中,通过基类指针调用虚函数时,确保基类指针指向的是派生类对象,以便调用正确的函数实现。否则可能会导致未定义的行为或者程序崩溃。

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

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

相关文章

数据结构与算法:动态规划(Dynamic Programming)详解

动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09; 是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的&#xff0c;通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划经常被用于求解优化问题。 动态规划的定…

【pytorch载入模型报错解决】Missing key(s) in state_dict、Unexpected key(s) in state_dict

当你试图加载模型参数时&#xff0c;爆出如下类似错误&#xff1a; Missing key(s) in state_dict: "conv1.weight", "bn1.weight", "bn1.bias", "bn1.running_mean", ... Unexpected key(s) in state_dict: "epoch", &quo…

【多线程】单例模式 | 饿汉模式 | 懒汉模式 | 指令重排序问题

文章目录 单例模式一、单例模式1.饿汉模式2.懒汉模式&#xff08;单线程&#xff09;3.懒汉模式&#xff08;多线程&#xff09;改进 4.指令重排序1.概念2.question:3.解决方法4总结&#xff1a; 单例模式 一、单例模式 单例&#xff0c;就是单个实例 在有些场景中&#xff0c…

Adobe Premiere 2020 下载地址及安装教程

Premiere是一款专业的视频编辑软件&#xff0c;由Adobe Systems开发。它为用户提供了丰富的视频编辑工具和创意效果&#xff0c;可用于电影、电视节目、广告和其他多媒体项目的制作。 Premiere具有直观的用户界面和强大的功能&#xff0c;使得编辑和处理视频变得简单而高效。它…

【高阶数据结构】哈希表 {哈希函数和哈希冲突;哈希冲突的解决方案:开放地址法,拉链法;红黑树结构 VS 哈希结构}

一、哈希表的概念 顺序结构以及平衡树 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系。因此在查找一个元素时&#xff0c;必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)&#xff1b;平衡树中为树的高度&#xff0c;即O(log_2 N)&#xf…

【笔记】Android 网络漫游更新网络状态、运营商名称等信息日志分析

业务知识 漫游有国内和国际漫游之分,Android代码定义如下: //frameworks/base/telephony/java/android/telephony/ServiceState.java/*** registered in a domestic roaming network* @hide*/@SystemApipublic static final int ROAMING_TYPE_DOMESTIC = 2;/*** registered…

Neo4j 图形数据库中有哪些构建块?

Neo4j 图形数据库具有以下构建块 - 节点属性关系标签数据浏览器 节点 节点是 Graph 的基本单位。 它包含具有键值对的属性&#xff0c;如下图所示。 NEmployee 节点 在这里&#xff0c;节点 Name "Employee" &#xff0c;它包含一组属性作为键值对。 属性 属性是…

Android 11.0 MTK平台关机充电动画横屏显示修改

1.前言 在11.0的系统rom定制化开发中,在关于MTK平台的产品中,系统默认的充电动画是竖屏显示的,但是在像平板的产品中竖屏动画肯定不符合规范, 所以需要在平板TV产品中,充电动画同时也是需要修改为横屏显示的,接下来就来分析下充电动画的相关绘制流程,然后实现功能 2.M…

go语言操作 PostgreSQL 数据库

1. Go 的 PostgreSQL 驱动程序 使用go get命令来安装 go get github.com/lib/pqPostgreSQL 数据库凭据&#xff1a;确保要连接的 PostgreSQL 数据库的地址、端口、用户名和密码 2. 导入必要的包 首先&#xff0c;在 Go 代码中导入所需的包&#xff0c;包括 PostgreSQL 驱动…

dcoker+nginx解决前端本地开发跨域

步骤 docker 拉取nginx镜像跑容器 并配置数据卷nginx.conf nginx.conf文件配置 这里展示server server {listen 80;listen [::]:80;server_name localhost;#access_log /var/log/nginx/host.access.log main;location / {# 当我们访问127.0.0.1:8028就会跳转到ht…

ubuntu20.04安装+ros-noetic安装+内网穿透frp

刷机后的系统安装 ubuntu20.04安装安装ros-noetic安装各种必要的插件安装vscode内网穿透连接实验室主机配置frpc和frps文件运行完成自动化部署免密登录linux的免密登录windows上的免密登录 内网穿透的参考链接&#xff1a;如何优雅地访问远程主机&#xff1f;SSH与frp内网穿透配…

Bootstrap 5 保姆级教程(一):容器 网格系统

一、容器 1.1 固定宽度&#xff08;.container&#xff09; .container 类用于固定宽度并支持响应式布局的容器。 以下实例中&#xff0c;我们可以尝试调整浏览器窗口的大小来查看容器宽度在不同屏幕中等变化&#xff1a; <!doctype html> <html lang"en&quo…

【C语言回顾】分支和循环

前言1. if 分支进阶1.1 嵌套 if1.2 悬空 else 2. switch 语句3. while 循环4. for 循环5. goto语句结语 上期回顾: 【C语言回顾】数据类型和变量相关 前言 各位小伙伴&#xff0c;大家好&#xff01;话不多说&#xff0c;我们直接进入正题。 以下是C语言分支和循环的总结。 1…

ARM看门狗定时器

作用 在S3C2440A中&#xff0c;看门狗定时器的作用是当由于噪声和系统错误引起的故障干扰时恢复控制器的工作。 也就是说&#xff0c;系统内部的看门狗定时器需要在指定时间内向一个特殊的寄存器内写入一个数值&#xff0c;俗称喂狗。 如果喂狗的时间过了&#xff0c;那么看门…

修改Ubuntu的镜像源为华为镜像源

修改Ubuntu的镜像源为华为镜像源 1、首先使用以下命令备份现有的镜像源&#xff1a; cd /etc/apt sudo cp sources.list sources.list.bak 2、使用以下命令打开镜像源文件&#xff1a; sudo vim /etc/apt/sources.list 3、在vim插入模式下使用以下内容替换掉原镜像源…

STM32H7上实现AD5758驱动

目录 概述 1 下载ADI 5758 Demo 2 AD5758驱动的移植 2.1 使用STM32CubeMX创建工程 2.2 接口函数实现 2.2.1 驱动接口列表 2.2.2 函数实现 2.2.3 修正ad5758驱动 3 AD5758应用程序 3.1 编写测试程序 3.1.1 配置参数结构 3.1.2 配置参数函数 3.1.3 读取参数函数 3.…

时隔一年,再次讨论下AutoGPT-安装篇

AutoGPT是23年3月份推出的&#xff0c;距今已经1年多的时间了。刚推出时&#xff0c;我们还只能通过命令行使用AutoGPT的能力&#xff0c;但现在&#xff0c;我们不仅可以基于AutoGPT创建自己的Agent&#xff0c;我们还可以通过Web页面与我们创建的Agent进行聊天。这次的AutoGP…

设计模式——观察者模式17

观察者模式指多个对象间存在一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式。 中介者模式是N对N的双向关系。观察者模式是1对N的单向关系。 设计模式&#xff0c;一定要敲代码…

电机驱动-理论学习-Fast计算

Fast计算 cordic方法原理详解代码实现 德州仪器IQmath算法讲解代码实现 欧洲黑客大会FastMath实现原理代码实现 电机运算通常在有限资源MCU上进行计算&#xff0c;对实时性有极高要求。然而&#xff0c;电机驱动又有大量的计算。所以&#xff0c;对运算速度也有较为严格要求。所…