导读:
用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中也实现类似的功能呢? (待续)
本文转自
但即使再纯粹的技术,弄得时间长了都会产生一些独特的想法和感受,记录这些想法可能不光对自己是有意义的。今天来写第一篇偏向技术的东西。
如何在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
http://blog.blogwhy.com/tpfly/e_23482.html