你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言实现面向对象编程OOP

[复制链接]
gaosmile 发布时间:2020-12-14 23:29
解释区分一下C语言和OOP
我们经常说C语言是面向过程的,而C++是面向对象的,然而何为面向对象,什么又是面向过程呢?不管怎么样,我们最原始的目标只有一个就是实现我们所需要的功能,从这一点说它们是殊途同归的。过程与对象只是侧重点不同而已。
举个例子吧,我现在有个计划,要去北京,OOP语言是直接给你一个车,然后你自己设定路线去北京就好,而C语言是需要你自己制造零件,自己组装好车,然后再自己设定路线,最后到达北京。C语言比较费劲,但是程序的效率很高。
过程&对象?
一个对象就是由或多或少的针对这个对象的过程构成的,当然其中是少不了必要的属性。
一个过程是针对一个或者是多个对象所进行的操作。两者是可以互相转换的,关键是哪一种方式更能适合你现在的需求,更能让你的软件开发锦上添花。
微信图片_20201214232525.png
我个人认为一般情况下,一个更容易扩展、维护的软件通常采用的是OOP的思想,添加一个原本不存在的相对无关单独的个体,总比在一个已经存在的过程内硬塞进去一个对象要简单;而且面向过程更容易导致混乱的维护。
举个例子,同样是一条河与一个湖泊,哪一个更容管理维护呢?我想答案是显而易见的。当然不管怎么样,软件本身设计架构的好坏也是非常重要的。
C语言的特性,实现OOP
C是一门面向过程的语言,但它依旧可以实现大多数面向对象所能完成的工作。比如面向对象的三大特性:封装、继承、多态。我们以下图来写代码举例子。
微信图片_20201214232529.png
封装
由于面象向对象是将数据与方法封装到一个类里。使用者无需关心类是怎么实现的。在 C_OOP 中贯彻了这一思想,C中有一种复杂的数据结构叫做struct。struct是C里面的结构体。
如上图假如我们要对鸟bird进行封装,bird可能包括姓名、颜色、栖息地、重量、属性等信息。我们就可以对它封装如下:
struct Bird{' A% L+ ^; Y) f# m0 [" e
    char name[20];//姓名
! {1 n/ n) a& a    char color;    //颜色   
9 }4 {) V. I" F& O+ c5 u% o    char addr[30];    //栖息地
) \) R/ f6 K" v$ O- E. u    int weight;        //体重
8 @+ b, ]; d( v" N! y$ V7 c( R9 S    int other;      //属性. @2 V9 O) I5 |+ a0 t  ~
};
4 a6 J5 }! `$ O* H1 k' Z3 D
当我们要像OOP那样新建一个对象时,我们就可以:
struct Bird p;3 i" G9 Q+ @: T' v7 C3 V
我们就可以直接对p进行赋值:
p.name = "bird";
; c8 v/ W7 [; U1 D+ ep.color = 'b';  //'b' = black; 'g' = green4 L9 M) t% N; P- R# n" c; p4 s
p.addr = 'w';  5 V/ f, @: s: u* z9 i- T3 R+ X
p.weight = 175;
$ B7 e& M& |; H; r) gp.other = 1;
; u$ D1 Z( E" G! ?3 j
继承
在常见用C语言实现继承的机制中,多半是用结构体组合实现的,同样利用struct,我们来创建一个Bird结构,同时继承结构体Bird,如下:
struct fBird{
) N  A+ J8 z4 x3 Z# V    struct Bird p;/ W1 r! m: g( f" J. `! }! M
    char fly[20]; //飞翔) B7 e8 z* h  n; ]: t
    int scream;        //鸣叫
3 v. j( H9 ]5 Z8 ?# `. G* w1 c};$ d2 m2 p; n0 |' M2 f, W3 R
对Bird进行创建对象,并赋值:
struct fBird s;- S$ i& e6 p$ H( b
s.p.name = "bird";
" V, ]  |4 c3 }  R& zs.p.color = 'b';
1 m/ g# L6 z8 j( B. o2 ks.p.other = 25;
$ r& E& X$ M* l4 U9 x5 n- Xs.p.weight = 65;# _  @8 \, @5 U. Q5 K( X1 Z* k
s.fly = "0618";! a/ ]  U+ H- F& ]
s.scream = 90;
: p9 _/ c3 ~$ H; n2 T9 M0 F6 }
多态
C_OOP中的一个核心就是多态,C中对于多态的实现可以借助函数指针来实现。为了简单起见,我们假设Bird这个结构体中,只有一个函数指针。
struct Bird{8 d9 d2 C# {5 E5 |6 P& X
    void (*print)(void *p);
6 a' d+ G8 c8 G% `4 A4 I};
1 F6 B  ^! p, D, K+ @$ z& J& N5 T( `( t2 z
struct fBird{% ~+ z9 _$ p6 F+ ]$ d) M$ g- W; _
    struct Bird p;+ U: L# a/ `1 z& p" v
};7 d4 L  ?/ E! ~0 q2 d+ ~% ~
而Bird和fBird这两个结构体的print函数实现如下:
void printBird(void *Bird){6 {5 q$ N# v6 W# s
    if(NULL == Bird)
* K' C5 b5 F7 s& q! l, i        return ;2 \$ Q" F% j, f+ U9 Z
    struct Bird *p = (struct Bird *)Bird;
$ Y  y* u- H& H, @! r    printf("run in the Bird!!\n");
: X* d- j7 `6 J- f9 Z0 U- A}
4 u& ?. I0 g: H: h% jvoid printfBird(void *Bird){7 n: _; k& I0 Y
    if(NULL == Bird)( f: b$ C8 }; h% m4 t' r8 m
        return ;
  D2 R. I* k7 ~/ n    struct Bird *p = (struct Bird *)Bird;1 Q0 v9 ~0 W& l- l. Y
    printf("run in the fBird!!\n");
  C' [/ T; Y; s' S}  ]1 `! V. V' H* U4 o  \
我们写一个函数来调用他们:
void print(void *Bird){* C- S: F7 A, c: B. ?; ?
    if(NULL == Bird)
( T0 a; J; Z' P  @8 {% t        return ;6 f8 Z; K* O$ z7 v. f
    struct Bird *p = (struct Bird *)Bird;
2 g+ g" s7 e+ k) Z& L* \' c    p->print(Bird);
' T5 L! F" z2 |$ r5 t: `}2 t' h3 P- i6 R! j
int main(){4 p, D0 o1 d6 K4 K
    struct Bird bird;
7 S4 U, D/ A4 O    struct fBird fbird;! l" r8 O1 u; a: h, T1 z! d, J
    Bird.print = printBird;
4 K0 f( t7 ]* U' B" O    fBird.p.print = printfBird;
; Y' H& ]) Q" ^- @. h% z  _5 \
    print(&bird);    //实参为Bird的对象% U8 K! [) R0 E. g# v; ^
    print(&fbird);  //实参为fBird的对象
" M9 _! F+ j, _/ o. n- Z) \( Y. h; X# V- Y" F0 j
    return 0;4 X) G" J& `2 X8 G( f! L1 x8 O! ?8 z
}% S# F# f1 U  U# u9 i6 O
他们的输出为:
run in the Bird!!# {' D( U, [4 e& x* @& s
run in the fBird!!& E9 k/ X, ]; x6 f3 O0 z
其实这个也不难理解,无论是fBird还是Bird,他们在内存中只有一个变量,就是那个函数指针,而void表示任何类型的指针,当我们将它强制转换成struct Bird类型时,p->print指向的自然就是传入实参的print地址。
OOP真的那么重要?
从大学到工作至今,在嵌入式领域中一直是使用C语言,而我在学习C++的过程中,看的代码越多,代码量越大,越来越觉得C++对于大型软件架构的良好可控性,和对以后程序员维护代码时良好的可读性;
个人认为:C语言中最大的成功在于它的指针,但是也是最容易出错的,想要理解C,必须要掌握指针。虽然说,语言只是一门工具,但是这是基础.
微信图片_20201214232534.jpg
或者你可以说C太底层,现在都是OOP的时代了,谁还会用面向过程的,你们不要忘了操作系统是用什么写的?是C;C实现的nginx的并发量是C++实现的apache的几十倍,关键是要理解语言背后的思想。
当然这不是为了OOP而OOP,实在是OOP的一些特征,例如封装,多态其实是软件工程思想,这些思想不分语言,遵循了这些思想可以使得程序更有弹性,更易修改和维护,避免僵化,脆弱的性质。
嵌入式C语言使用OOP的一些思考
然而就目前来说,在嵌入式领域广泛的使用C++显然是不现实的事情。在一个到处是OOP的年代,为何面向过程的C语言依然可以如此活跃?
我们可以用它来开发一系列的小工具,Unix/Linux就是由这些小工具组成的操作系统;同时用C语言可以开发高性能的应用程序。
C语言良好的可移植性,小巧灵活,而且还有一个直接与硬件打交道的指针的存在,对内存等良好的操作性以及执行之速度快,是嵌入式开发唯有的高级语言,均是一般嵌入式产品的不二首选。
LW_OOPC->C语言的面对对象
LW_OOPC是台湾的MISOO团队根据多年研发经验,总结出来的一种轻便的面向对象的C语言。虽然不足以提供足够的能力使我们实现面向对象所有的概念,但是我们依然可以应用它们完成我们简单的面向对象思想的构建。
lw_oopc仅用了2个文件,.h及.c文件就实现了面向对象的三大因素,实现过程极为简洁又富含技巧。lw_oopc说白了,就是定义了一堆宏,使用起来也就是调用这些宏。
//| INTERFACE                 | 接口
1 M. U- S' r" r/ _8 u* e//----------------------------------------------------------------------3 Z( i  V. ~% F( J' [& s8 k
//| CLASS                     | 类6 h9 }  h  n% i7 k. Y$ c
//----------------------------------------------------------------------6 @8 j* q8 N6 e- h- A: ?' l# d
//| CTOR                      | 构造器开始
9 P8 R/ }+ f9 P- {7 m8 H//----------------------------------------------------------------------
* m7 X# F5 O% X2 D/ w//| END_CTOR                  | 构造器截止  }5 N# ]; W2 [' _: X
//----------------------------------------------------------------------
1 f. ^( V; G7 ~+ W3 t//| FUNCTION_SETTING          | 关联成员函数指针
. P" c. [+ i: R  j5 [- w//----------------------------------------------------------------------- M1 ?* W) }! \, o2 P& O/ H9 v& e! [
//| IMPLEMENTS                | 继承
: G7 R, k, Z; ?7 }0 x7 u//----------------------------------------------------------------------/ b4 K3 A9 K' P7 s/ V
//| DTOR                      | 为了支持析构函数的概念 0 e0 |: ?# t# X# v
//| END_DTOR                  |                                                   
8 Q* ~0 t' _* C: R/ H//----------------------------------------------------------------------
! t% N' @, X4 L$ A+ Q& b& z//| ABS_CLASS                 | 为了支持抽象类的概念   * ~+ A& c  a* `' e" ]$ m
//----------------------------------------------------------------------
* _0 Y) _. N- O//| ABS_CTOR                  | 为了支持可继承的抽象类的构造函数 ) s+ J; F3 r2 c; _, o. s% e+ ?
//| END_ABS_CTOR              |                        
: q0 j4 |  I/ x, \8 l//----------------------------------------------------------------------
9 ~' h; f" |. g7 _; w" n//| EXTENDS                   | 为了让熟悉Java的人容易理解(与IMPLEMENTS宏等同)  
. B. t1 i: ?- s: Y//----------------------------------------------------------------------' y$ @! b7 w8 B+ d& y% @
//| SUPER_CTOR                | 为了支持子类调用父类的构造函数  p& a0 T# g4 j

3 x  S9 z2 `, n' A! {, _//----------------------------------------------------------------------7 o2 X3 g: F& X. E, @. v
//| SUPER_PTR                 | 为了支持向上转型     . `: O: n* s& r+ I5 g
//| SUPER_PTR_2               |     
) f) w: e6 u. i8 D6 C# T//| SUPER_PTR_3               | 6 n/ K5 J1 R/ k: j
//----------------------------------------------------------------------" ]9 ?6 v$ h8 |- k) O! V+ B- i) W& y
//| SUB_PTR                   | 为了支持向下转型   
/ N6 r1 T/ L: _" L5 ^//| SUB_PTR_2                 |                       
6 y2 [0 ]8 ?( {8 r8 ^3 _1 D//| SUB_PTR_3                 |                                          
  m* j, e; r. B# T' a! O8 ]3 \- S//----------------------------------------------------------------------: r0 w* K+ y3 T6 J
//| INHERIT_FROM              | 为了支持访问直接父类的数据成员! @. o( p* P  K/ t' n3 N
//----------------------------------------------------------------------4 t# J% @8 w3 E! b1 }: b4 Y! {7 W
下面是对LW_OOPC的简单的分析。
LW_OOPC概述
简单来说它主要是一个头文件,我们通过对这个头文件的使用来实现面向对象。
//lw_oopc.h : MISOO团队设计的C宏9 i5 A- X: `% i% N& m; U( L' O/ S5 f
#include
$ _$ ~$ c7 U. M; {2 V/ i
, U; L9 K& c; @1 X5 Y6 R#ifndef LW_OOPC/ {7 ~  {; ]3 s% s
#define LW_OOPC+ T' [1 M2 G6 K8 S! C# ]

( b0 }- d7 J7 @#define CLASS(type)       /4 E% t. J+ B. U( T1 b
typedef struct type type; /
4 C3 K; B) \7 V4 g2 a, ^& tstruct type1 Q0 c' y8 u1 C; Y& q! r
6 `* J. `' l" u$ A
#define CTOR(type)        /
. ~. J9 a$ F# kvoid* type##New()         /
& H  O+ ~* t: i/ S{                         /
0 D& o) R/ P" v3 P. Q6 c! E  struct type *t;        /+ l. h4 _  \% \9 n; e# k
  t = (struct type*)malloc(sizeof(struct type));
- v7 _7 n( m0 M& r
( u. l& q" r6 B+ W$ f( x- o. X0 P/ ?3 ]#define CTOR2(type, type2)     /
1 ~, M6 w, i, F" `void* type2##New()             /' M9 n! k) V" i& N
{                              /8 m3 x& M  q: w5 @* h
  struct type *t;             /
) [+ L, I( E2 M, }, c4 k  t = (struct type*)malloc(sizeof(struct type));  
6 P/ K3 B( X1 `! u9 Q. I) n  [  R8 K9 b' D6 _& {
#define END_CTOR return (void*)t; }' S2 S3 U' k, }& q$ @
#define FUNCTION_SETTING(f1, f2) t->f1 = f2;
* E  c6 o, z; I6 W- I1 m& p( g#define IMPLEMENTS(type) struct type type7 N! H$ g( J/ G+ G
#define INTERFACE(type) struct type; }9 g) @( J  K7 j+ h
& _# b% z9 }' V
#endif; d) m( J* \% {: W
/*          lw_oopc.h               */, i4 a9 D3 O6 z! w3 w: a3 P2 M
下面一段代码是简单的OOPC的应用:
// Circle.c   
  c* e' \: w: @. s' a+ Q#include
2 E1 R9 }6 F$ N. x#include "lw_oop.h"6 n7 g/ u9 w: o% [+ ]3 |

  a$ Z$ v5 d! T5 g: K2 q2 D#define PI 3.1415926
2 I0 o! C5 I$ R
5 Z0 \" V) I/ R- V0 [& i* R6 c% sCLASS(Circle)
6 v# N: \5 P& I/ E+ {% O{
+ n; |4 d* A! Y/ f  double (*cal_area)(double);
+ A( r6 `  X/ A1 _8 u- {" F}
6 f( c$ E, c. ~9 d  c( r" u$ c, @4 S: y6 ?2 r9 B8 w
double circle_cal_area(double radius)
/ D) I6 \8 h  h2 r3 K1 P& E$ ]{4 o6 B6 b3 F! U1 c. D& j  U2 W
  return PI*r*r;
0 N- t( r1 v* O: f5 W- b; _; I}6 [3 U; I' y9 ~+ o

( `/ l/ b2 |7 ~& A  tCTOR(Circle)8 D  ~$ g) |5 @* a9 l6 I
FUNCTION_SETTING(cal_area, circle_cal_area); R4 x- I6 X3 e* Q' L+ w) {8 A$ L! e
END_CTOR$ F& F. i( [3 t! M9 Z9 w8 W2 b

4 t* p# N9 G3 I/ M5 Bint main()7 x; k  M' u# _$ s" S2 ^
{( a$ z4 ~. u/ a: d
     double area = 0.0;
8 X; i/ \# _( n     Circle *pc;8 K& U0 o% I( \6 O1 \: k
    ! A8 B; ~6 R/ @2 d/ I) i3 p
     pc = (Circle*)CircleNew();
1 b' L2 q9 F. L* B( N, ?  ^     area = pc->cal_area(10);
: P- v# g4 ^; {! f8 L# u( Y( b$ d, |& B  \7 i$ t  `
     printf("area = %f/n", area);" F% N$ Y/ Z- }/ D0 B, s2 O6 e2 j

1 L3 I6 j& z  r3 k; _( q0 V     return 0;
. M9 q0 X" ^# d1 Q: l; c" ?! a}3 _- B0 C  ]# B$ }* H
接口的实现
在OOP程序中,通常是通过类定义和接口定义来实现的。
//IA.h  : U" L0 [8 u' @, }
#include "lw_oopc.h"
4 H$ }  X' ^) p4 J5 f2 r! [7 U6 f; ^1 b& S2 E# T
INTERFACE(IA)
# m8 ?$ J5 i+ }; x( @$ D{$ [$ O0 @  [* \; g* C
   void   (*init)(void*, double);
' U; f( T+ K" k+ w) D   double (*cal_area)(void*);! b; B3 ^4 E7 v8 n
   double (*cal_permimeter)(void*);8 T7 R- C$ K! Y) F& |: v& ~
}& [) h, \$ W2 u
//circle.c
% _" f7 B- ^/ b( K9 o( r  t( c/ ^# e#include "IA.h"7 B" P' @4 c' [5 z, [& }
% a0 v* o: q! e. J+ m
#define PI 3.1415926( l" i, d: {& c8 p  P& a  P# ~
9 ]: |" p4 X8 R" J2 l0 C, o/ A  q
CLASS(Circle)( ~' V) ~; v; y1 x; ^( D; h
{
9 g% H% f1 q2 X3 J( G. L- S! K, L  IMPLEMENTS(IA);7 E6 P( ^! X7 ]' |
  double radius;
; Q* T6 s1 @# S0 m; \}8 {' |3 O% V0 y9 f  O9 T
, u9 D3 P! Z8 ?$ m* O4 K: ]) C
static void circle_init(void* circle, double radius)4 \: i8 m: E7 S
{5 j/ M" d% n6 T1 H
  Circle *_this = (Circle*)circle;
  M8 y2 C# B7 L3 M! I2 n$ o6 B$ ]+ N
  _this->radius = radius;
8 N! v% _7 g% O( z8 h}- m& _& ]4 I  ?0 k- H# G  X: [) G2 ~

) P2 _/ ~. V" A" P2 Q) jstatic double circle_cal_area(void* circle)
$ U$ l# U2 K2 C# i8 _  R5 }% t" Z{0 M2 e& A1 u* p6 m
  Circle *_this = (Circle*)circle;
8 L/ L1 R) w% M( k& m8 D' z  U' r% q2 C7 i; k
  return PI*_this->radius*_this->radius;; R; N6 z- j: H1 J
}
: {% C' }( @1 j4 P
) X: }2 T* L& T+ V2 `/ lstatic double circle_cal_permimeter(void* circle)
" v) ~: t0 }; S* T{
* b$ i, m5 Q! H, d9 \& I% V- s! X  Circle *_this = (Circle*)circle;
# g5 v" e# m; }' |/ j' a' j. h( s: H7 U; C$ X# A
  return 2*PI*_this->radius;0 G. t  a3 y: w/ d/ D8 C
}
& g$ C2 I& x5 x# j6 e" l0 Z
# C- O/ n& N, H. f* ~5 V# tCTOR(Circle)
) ?' Y" [0 I; ~& h/ G' J1 `- {  FUNCTION_SETTING(IA.init, circle_init)7 F& j& U( z& c
  FUNCTION_SETTING(IA.cal_area, circle_cal_area)
7 L" [. W4 S2 [8 y, M0 c8 ^) V7 c  FUNCTION_SETTING(IA.cal_permimeter, circle_cal_permimeter)( w' R  U3 G" t
END_CTOR. A$ ]# ?& H& y+ X! {- m/ U: @
//main.c: a, e) |  a- a3 r/ @% p
#include
- U. C# N: N3 u) P9 ]( N#include “IA.h”8 F2 I1 e2 X! k5 [
* w/ i/ _3 q/ z
void print_area(IA* pi)
' Q: d: \/ s6 }3 t# g2 y{
1 J- |8 C! E$ L4 p9 W1 T    printf("area = %f/n", pi->cal_area(pi));
: O: p* V+ Q- C  _4 g}. r7 e6 a. q1 O4 W3 j

. e7 ]4 T- h2 G; L9 Y! V% i( Oint main()
' i& w" J2 F8 P5 D: h- f" k{# A6 S6 l! y. s6 R" L% e7 t
  IA *pc = NULL;' t: r( U  u, b  P; x5 v+ `

: h+ X3 U/ b- [. [* h  pc = (IA*)CircleNew();
( F2 `9 F; a! U0 a; A  pc->init(pc, 10.0);
# e" E7 X# ^" W6 h: @3 H
4 B7 p+ r* A; F, i  print_area();
9 ]! Z' v# S) y- m: T  return 0;
8 t4 t  a4 [: F7 A: f}
. u- c+ S; F7 V
总结
语言只是一种工具,任何语言之间都是相通的,一通则百通,关键是要理解语言背后的思想,理解其思想,任何语言,拿来用就行了。语言没有好坏之分,任何语言既然存在自然有它存在的价值。

/ ~& P8 V. ?* l4 _
赞 收藏 评论0 发布时间:2020-12-14 23:29

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版