用C语言模拟面向对象编程(上)

简介: 用C语言模拟面向对象编程(上) 导读:    用C语言模拟面向对象编程    虽然我接触计算机已经有将近一年了,但一直以来我不愿意写技术方面的文档,因为所谓的“技术”就是照着做得东西, 即使可能遇到一些难题,到网上搜索一下就可以解决,不值得把网上长篇累牍的文档复制粘贴到这里来。

用C语言模拟面向对象编程(上)


导读: 
  用C语言模拟面向对象编程 
  虽然我接触计算机已经有将近一年了,但一直以来我不愿意写技术方面的文档,因为所谓的“技术”就是照着做得东西, 即使可能遇到一些难题,到网上搜索一下就可以解决,不值得把网上长篇累牍的文档复制粘贴到这里来。即使是自己写,也是写些别人已经解决过的东西。所以不论是数学,还是计算机,在这里我没有写过那种简单搬运知识的文章。 
  但即使再纯粹的技术,弄得时间长了都会产生一些独特的想法和感受,记录这些想法可能不光对自己是有意义的。今天来写第一篇偏向技术的东西。 
  如何在C语言中用面向对象的思路写程序?这已经是很多人考虑过的问题了,而且实际上已经有人在用C语言实现一些面向对象的项目了,比如,在Linux下大名鼎鼎的图形界面GNOME,就是通过C语言模拟的面向对象特性来实现的。 
  在一本名叫《面向对象编程,C++和Java比较教程》的书中有一个抛砖引玉的例子,代码如下(见该书中文版第67页,其中注释块中是自己的评注): 
  /* SimulatedOO.c */ 
  #include 
  #include 
  #include 
  /* 基类型: */ 
  typedef struct 
  { 
  char * name; 
  int age; 
  } User; 
  /* 子类型: */ 
  typedef struct 
  { 
  User genericUser; 
  char ** listOfCourses; 
  int numCourses; 
  int whatYear; 
  } StudentUser; 
  /********************************************************* 
  * 这样, 在子类型 StudentUser 中就有一个基类 User 的结构体 
  * 变量, 并且在内存中 User 结构体占据 StudentUser 的开头部分, 
  * 这样,把一个指向 StudentUser 类型的指针强制类型转换到 User* 
  * 类型,就可以访问到 User 的相应区域,这样就实现了继承机制。 
  ********************************************************/ 
  /* 为基类型User定义的函数: */ 
  void setUserName( User* user,char aName[] ) { 
  user -> name = malloc(strlen(aName)+1); /* 原文为 malloc( sizeof(strlen(aName)+1) ), 这是不正确的。*/
  strcpy(user->name,aName); 
  } 
  /* 为基类型User定义的函数: */ 
  char * getUserName( User * user) { 
  printf("/nName of user: %s/n",user->name); 
  return user->name; 
  } 
  /* 为基类型User定义的函数: */ 
  void setUserAge(User * user,int yy) { 
  user -> age=yy; 
  } 
  /* 为基类型User定义的函数: */ 
  int getUserAge( User * user ) { 
  printf("%s's age: %d/n",user->name,user->age); 
  return user-> age; 
  } 
  /* 为基类型User定义的函数: */ 
  /************************************************** 
  * 这个函数在书中是用来展示多态行为的,不过这种实现多态的 
  * 方式并不是很好,并不能实现类似于C++或Java中多态的动态连 
  * 接机制。具体用法见主程序中注释为“多态行为” 那两行。 
  *************************************************/ 
  int isSenior( User * user ) { 
  if ( user->age >70 ) return 1; 
  else return 0; 
  } 
  /* 为子类型 StudentUser 定义的函数: */ 
  void setListOfCourses( StudentUser* student,char* listCrs[],int nCourses) { 
  int i; 
  char ** temp; 
  student->numCourses = nCourses; 
  temp = malloc( nCourses * sizeof( char* )); 
  student->listOfCourses=temp; 
  for(i=0;i numCourses;i++) { 
  *temp = malloc( strlen( *listCrs )+1); 
  strcpy(*temp,*listCrs); 
  *temp++; 
  listCrs++; 
  } 
  } 
  /* 为子类型 StudentUser 定义的函数: */ 
  void printListOfCourses( StudentUser * student ) { 
  int i; 
  char ** temp; 
  temp = student->listOfCourses; 
  printf("/n%s's courses: /n",student->genericUser.name); 
  for(i=0;i numCourses;i++) 
  printf("%s/n",*(temp++)); 
  } 
  /* 为子类型 StudentUser 定义的函数: */ 
  void setYear( StudentUser* student,int yy) { 
  student->whatYear=yy; 
  } 
  /* 为子类型 StudentUser 定义的函数: */ 
  int getYear( StudentUser* student) { 
  return student->whatYear; 
  } 
  int main() 
  { 
  User * zaphod; 
  StudentUser* trillian; 
  char * listCourses[]={"Physics","Chemistry","algebra"}; 
  int numCrs = sizeof(listCourses)/sizeof(listCourses[0]); 
  zaphod = malloc(sizeof(User)); 
  setUserName(zaphod,"Zaphod"); 
  setUserAge(zaphod,89); 
  getUserName(zaphod); 
  getUserAge(zaphod); 
  trillian=malloc(sizeof(StudentUser)); 
  setUserName((User*)trillian,"Trillian" ); 
  setUserAge((User*)trillian,18); 
  getUserName((User*)trillian); 
  getUserAge((User*)trillian); 
  setListOfCourses(trillian,listCourses,numCrs); 
  printListOfCourses(trillian); 
  /*  多态行为 */ 
  printf("/nZaphod is senior is %s/n",isSenior(zaphod)? "true":"false"); 
  printf("/nTrillian is senior is %s/n", isSenior((User*)trillian)? "true":"false"); 
  /***************************************************** 
  * 和setUserAge 、setUserName之类的函数一样,isSenior 
  * 需要一个基类指针参数,通过将 trillian 转换为 User* 
  * 类型,就会把指向trillian 中User域的指针传进函数。 
  * ***************************************************/ 
  printf("name field of trillian is at address: %p/n", &( trillian->genericUser.name )); 
  printf("trillian when cast to User* is at address: %p/n",(User*)trillian ); 
  /*************************************************** 
  * 这两行输出的地址值是一样的。 
  * *************************************************/ 
  } 
  在C++和Java中,多态行为是由一种动态连接机实现的,比如,在C++中定义如下的类 Base 和它的子类 Sub: 
  class Base { 
  int data; 
  public: 
  Base() : data(3) {} 
  virtual int getData() const { 
  return data; 
  } 
  }; 
  class Sub:public Base { 
  int data; 
  public: 
  Sub() : data(5) {} 
  int getData() const { 
  return data; 
  } 
  }; 
  那么如果有一个Base 类型的指针指向了一个Sub类,通过这个指针调用getData()时将返回子类Sub中的data:初始值5。这样,如果有一个储存基类型指针的数组,但这些指针有的指向基类,有的指向子类,那么我就可以通过指针统一地调用 getData() 函数,依然能够得到正确的值。 
  怎么在C中也实现类似的功能呢? (待续) 

本文转自 
http://blog.csdn.net/chief1985/article/details/2008479
http://blog.blogwhy.com/tpfly/e_23482.html

目录
相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
38 3
|
15天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
37 10
|
15天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
37 9
|
15天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
30 8
|
15天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
39 6
|
15天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
90 6
|
15天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
47 6
|
15天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
24 5
|
26天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
48 4
|
1月前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
32 6