[c] C로 객체 지향 코드를 작성하는 방법은 무엇입니까? [닫은]

C로 객체 지향 코드를 작성하는 몇 가지 방법은 무엇입니까? 특히 다형성과 관련하여.


이 스택 오버플로 질문 Object-orientation in C 도 참조하십시오 .



답변

예. 실제로 Axel Schreiner는 자신의 저서 “ANSI-C의 객체 지향 프로그래밍”을 무료로 제공하며이 주제를 매우 철저히 다루고 있습니다.


답변

다형성에 대해 이야기하고 있기 때문에 C ++이 나오기 몇 년 전에 그런 일을하고있었습니다.

기본적으로 a struct를 사용 하여 데이터와 함수 포인터 목록을 모두 보유하여 해당 데이터의 관련 기능을 가리 킵니다.

따라서 통신 클래스에서는 객체에 대한 데이터와 함께 구조에서 4 개의 함수 포인터로 유지되는 공개, 읽기, 쓰기 및 닫기 호출이 있습니다.

typedef struct {
    int (*open)(void *self, char *fspec);
    int (*close)(void *self);
    int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    // And data goes here.
} tCommClass;

tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

물론, 위의 코드 세그먼트는 실제로 다음과 같은 “생성자”에 있습니다 rs232Init().

해당 클래스에서 ‘상속’할 때 포인터를 자신의 함수를 가리 키도록 변경하면됩니다. 이러한 함수를 호출 한 사람은 모두 함수 포인터를 통해 다형성을 얻을 수 있습니다.

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

수동 vtable과 같습니다.

포인터를 NULL로 설정하여 가상 클래스를 가질 수도 있습니다. 동작은 C ++ (컴파일 타임 오류가 아니라 런타임시 코어 덤프)과 약간 다릅니다.

다음은이를 보여주는 샘플 코드입니다. 먼저 최상위 클래스 구조 :

#include <stdio.h>

// The top-level class.

typedef struct sCommClass {
    int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;

그런 다음 TCP ‘subclass’에 대한 함수가 있습니다.

// Function for the TCP 'class'.

static int tcpOpen (tCommClass *tcp, char *fspec) {
    printf ("Opening TCP: %s\n", fspec);
    return 0;
}
static int tcpInit (tCommClass *tcp) {
    tcp->open = &tcpOpen;
    return 0;
}

그리고 HTTP도 마찬가지입니다 :

// Function for the HTTP 'class'.

static int httpOpen (tCommClass *http, char *fspec) {
    printf ("Opening HTTP: %s\n", fspec);
    return 0;
}
static int httpInit (tCommClass *http) {
    http->open = &httpOpen;
    return 0;
}

마지막으로 테스트 프로그램이 실제로 작동하는지 보여줍니다.

// Test program.

int main (void) {
    int status;
    tCommClass commTcp, commHttp;

    // Same 'base' class but initialised to different sub-classes.

    tcpInit (&commTcp);
    httpInit (&commHttp);

    // Called in exactly the same manner.

    status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
    status = (commHttp.open)(&commHttp, "http://www.microsoft.com");

    return 0;
}

출력이 생성됩니다.

Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com

하위 클래스에 따라 다른 함수가 호출되고 있음을 알 수 있습니다.


답변

네임 스페이스는 종종 다음을 수행하여 수행됩니다.

stack_push(thing *)

대신에

stack::push(thing *)

C 구조체를 C ++ 클래스 와 같은 것으로 만들려면 다음을 수행하십시오.

class stack {
     public:
        stack();
        void push(thing *);
        thing * pop();
        static int this_is_here_as_an_example_only;
     private:
        ...
};

으로

struct stack {
     struct stack_type * my_type;
     // Put the stuff that you put after private: here
};
struct stack_type {
     void (* construct)(struct stack * this); // This takes uninitialized memory
     struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
     void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
     thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
     int this_is_here_as_an_example_only;
}Stack = {
    .construct = stack_construct,
    .operator_new = stack_operator_new,
    .push = stack_push,
    .pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else

그리고 :

struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
   // Do something about it
} else {
   // You can use the stack
   stack_push(st, thing0); // This is a non-virtual call
   Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
   st->my_type.push(st, thing2); // This is a virtual call
}

소멸자 또는 삭제하지 않았지만 동일한 패턴을 따릅니다.

this_is_here_as_an_example_only는 유형의 모든 인스턴스에서 공유되는 정적 클래스 변수와 같습니다. 일부는 이것을 취하는 것을 제외하고는 모든 메소드는 실제로 정적입니다 *


답변

C에서 OOP를 구현하는 것은 OOP를 구현하는 것이 OOP를 배우고 내부 작업을 이해 하는 훌륭한 방법이라고 생각합니다 . 많은 프로그래머의 경험에 따르면 기술을 효율적이고 자신있게 사용하려면 프로그래머가 기본 개념이 어떻게 구현되는지 이해해야합니다. C에서 클래스, 상속 및 다형성을 에뮬레이트하면 바로 이것을 가르칩니다.

원래 질문에 대답하기 위해 C에서 OOP를 수행하는 방법을 가르치는 몇 가지 자료가 있습니다.

EmbeddedGurus.com 블로그 게시물 “C의 객체 기반 프로그래밍”은 이식 가능한 C에서 클래스 및 단일 상속을 구현하는 방법을 보여줍니다.
http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /

애플리케이션 노트 “”C + “-C의 객체 지향 프로그래밍”은 전 처리기 매크로를 사용하여 C에서 클래스, 단일 상속 및 후기 바인딩 (다형성)을 구현하는 방법을 보여줍니다 (
http://www.state-machine.com/resources/cplus_3). 0_manual.pdf 의 예제 코드는 http://www.state-machine.com/resources/cplus_3.0.zip 에서 볼 수 있습니다 .


답변

나는 그것을 보았다. 나는 그것을 추천하지 않을 것입니다. C ++은 원래 C 코드를 중간 단계로 생성하는 전처리기로 시작되었습니다.

본질적으로 당신이하는 일은 함수 참조를 저장하는 모든 메소드에 대한 디스패치 테이블을 만드는 것입니다. 클래스를 파생 시키려면이 디스패치 테이블을 복사하고 재정의하려는 항목을 대체하고 새 “메소드”가 기본 메소드를 호출하려는 경우 원래 메소드를 호출해야합니다. 결국 C ++을 다시 작성하게됩니다.


답변

물론 가능합니다. 이것이 GTK +그놈 이 기반으로 하는 프레임 워크 인 GObject가하는 일 입니다.


답변

C stdio FILE 서브 라이브러리는 성인 C에서 추상화, 캡슐화 및 모듈화를 작성하는 방법에 대한 훌륭한 예입니다.

상속과 다형성 (OOP에 필수적으로 고려되는 다른 측면)은 반드시 생산성 향상을 제공 할 필요는 없으며 실제로 문제 영역에 대한 개발과 사고를 방해 할 수 있다는 합리적인 주장이루어졌습니다 .