답변:
예. 실제로 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
하위 클래스에 따라 다른 함수가 호출되고 있음을 알 수 있습니다.
tCommClass
이름이로 바뀌고 구조체에는 데이터 필드와 단일 필드가 " 단일 "가상 테이블을 가리키는 것만 있습니다. 각 인스턴스와 함께 모든 포인터를 수행하면 불필요한 오버 헤드가 추가되고 C ++, IMHO보다 JavaScript에서 작업하는 방법과 더 유사합니다. tCommVT
tCommClass
tCommVT vt
네임 스페이스는 종종 다음을 수행하여 수행됩니다.
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는 유형의 모든 인스턴스에서 공유되는 정적 클래스 변수와 같습니다. 일부는 이것을 취하는 것을 제외하고는 모든 메소드는 실제로 정적입니다 *
st->my_type->push(st, thing2);
대신st->my_type.push(st, thing2);
struct stack_type my_type;
struct stack_type * my_type;
Class
구조체는 어떻습니까? OO C를 C ++보다 더 역동적으로 만들 수 있습니다. 어떻게에 대한? 그건 그렇고, +1.
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 ++을 다시 작성하게됩니다.
glib
객관적인 방식으로 C로 작성 되지 않습니까?
물론 가능합니다. 이것이 GTK + 와 그놈 이 기반으로 하는 프레임 워크 인 GObject가하는 일 입니다.
Animal and Dog의 간단한 예 : C ++의 vtable 메커니즘을 반영합니다 (대부분). 또한 malloc ()을 여러 번 호출하지 않도록 할당과 인스턴스화 (Animal_Alloc, Animal_New)를 분리합니다. 또한 this
포인터를 명시 적으로 전달해야합니다 .
비가 상 기능을 수행한다면, 그것은 사소한 일입니다. vtable에 추가하지 않고 정적 함수에는 this
포인터가 필요하지 않습니다 . 다중 상속에는 일반적으로 모호성을 해결하기 위해 다중 vtable이 필요합니다.
또한 setjmp / longjmp를 사용하여 예외 처리를 수행 할 수 있어야합니다.
struct Animal_Vtable{
typedef void (*Walk_Fun)(struct Animal *a_This);
typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);
Walk_Fun Walk;
Dtor_Fun Dtor;
};
struct Animal{
Animal_Vtable vtable;
char *Name;
};
struct Dog{
Animal_Vtable vtable;
char *Name; // Mirror member variables for easy access
char *Type;
};
void Animal_Walk(struct Animal *a_This){
printf("Animal (%s) walking\n", a_This->Name);
}
struct Animal* Animal_Dtor(struct Animal *a_This){
printf("animal::dtor\n");
return a_This;
}
Animal *Animal_Alloc(){
return (Animal*)malloc(sizeof(Animal));
}
Animal *Animal_New(Animal *a_Animal){
a_Animal->vtable.Walk = Animal_Walk;
a_Animal->vtable.Dtor = Animal_Dtor;
a_Animal->Name = "Anonymous";
return a_Animal;
}
void Animal_Free(Animal *a_This){
a_This->vtable.Dtor(a_This);
free(a_This);
}
void Dog_Walk(struct Dog *a_This){
printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}
Dog* Dog_Dtor(struct Dog *a_This){
// Explicit call to parent destructor
Animal_Dtor((Animal*)a_This);
printf("dog::dtor\n");
return a_This;
}
Dog *Dog_Alloc(){
return (Dog*)malloc(sizeof(Dog));
}
Dog *Dog_New(Dog *a_Dog){
// Explict call to parent constructor
Animal_New((Animal*)a_Dog);
a_Dog->Type = "Dog type";
a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;
return a_Dog;
}
int main(int argc, char **argv){
/*
Base class:
Animal *a_Animal = Animal_New(Animal_Alloc());
*/
Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());
a_Animal->vtable.Walk(a_Animal);
Animal_Free(a_Animal);
}
추신. 이것은 C ++ 컴파일러에서 테스트되었지만 C 컴파일러에서 쉽게 작동하도록해야합니다.
typedef
A는 내부에 struct
C로 할 수 없습니다
이것은 흥미로웠다. 나는 같은 질문을 스스로 숙고 해 왔으며 그것에 대해 생각할 때의 이점은 다음과 같습니다.
비 OOP 언어로 OOP 개념을 구현하는 방법을 상상하려고하면 OOp 언어 (제 경우 C ++)의 장점을 이해할 수 있습니다. 이를 통해 특정 유형의 응용 프로그램에 대해 C 또는 C ++를 사용할지 여부를 판단 할 수 있습니다. 한 응용 프로그램의 이점이 다른 응용 프로그램보다 중요합니다.
이것에 대한 정보와 의견을 웹에서 탐색하면서 임베디드 프로세서 용 코드를 작성하고 C 컴파일러 만 사용할 수있는 저자를 발견했습니다. http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C- 창조-클래스-파트 -1
그의 경우에는 일반 C에서 OOP 개념을 분석하고 적용하는 것이 타당했습니다. C에서 구현하려고 시도한 결과 오버 헤드 히트로 인해 일부 OOP 개념을 희생 할 수있는 것으로 보입니다.
내가 얻은 교훈은, 어느 정도 할 수 있으며, 시도해야 할 몇 가지 이유가 있습니다.
결국, 머신은 스택 포인터 비트를 돌리면서 프로그램 카운터를 건너 뛰고 메모리 액세스 작업을 계산합니다. 효율성 측면에서 볼 때, 귀하의 프로그램이 수행하는 계산의 수가 적을수록 좋습니다. 그러나 때때로 우리는이 세금을 단순히 지불해야만 인적 오류에 가장 취약한 방식으로 프로그램을 구성 할 수 있습니다. OOP 언어 컴파일러는 두 가지 측면을 모두 최적화하려고 노력합니다. 프로그래머는 이러한 개념을 C와 같은 언어로 구현하는 데 훨씬 신중해야합니다.
Core Foundation API 세트에 대한 Apple의 설명서를 보는 것이 도움이 될 수 있습니다. 순수한 C API이지만 많은 유형이 Objective-C 객체와 동등합니다.
Objective-C 자체의 디자인을 보는 것이 도움이 될 수도 있습니다. 객체 시스템이 C 함수와 관련하여 정의된다는 점에서 C ++과는 조금 다릅니다. 예 objc_msg_send
를 들어 객체에서 메소드를 호출하는 것입니다. 컴파일러는 대괄호 구문을 해당 함수 호출로 변환하므로 알 필요는 없지만 질문을 고려하면 후드에서 어떻게 작동하는지 배우는 것이 유용 할 수 있습니다.
사용할 수있는 몇 가지 기술이 있습니다. 가장 중요한 것은 프로젝트를 나누는 방법입니다. 프로젝트에서 .h 파일로 선언 된 인터페이스와 .c 파일에서 객체의 구현을 사용합니다. 중요한 부분은 .h 파일을 포함하는 모든 모듈이 객체 만으로 간주 void *
하고 .c 파일은 구조의 내부를 아는 유일한 모듈이라는 것입니다.
예를 들어 FOO라는 클래스의 클래스는 다음과 같습니다.
.h 파일에서
#ifndef FOO_H_
#define FOO_H_
...
typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */
/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif
C 구현 파일은 그런 식입니다.
#include <stdlib.h>
...
#include "FOO.h"
struct FOO_type {
whatever...
};
FOO_type *FOO_new(void)
{
FOO_type *this = calloc(1, sizeof (FOO_type));
...
FOO_dosomething(this, );
return this;
}
따라서 해당 모듈의 모든 기능에 대한 객체에 대한 포인터를 명시 적으로 제공합니다. C ++ 컴파일러는 암시 적으로 수행하며 C에서는 명시 적으로 작성합니다.
this
내 프로그램에서 C ++로 컴파일되지 않도록하고 구문 강조 편집기에서 다른 색상의 훌륭한 속성을 갖도록하기 위해 실제로 프로그램에서 사용 합니다.
FOO_struct의 필드는 한 모듈에서 수정 될 수 있으며 다른 모듈은 여전히 사용 가능하도록 다시 컴파일 할 필요가 없습니다.
이러한 스타일로 OOP (데이터 캡슐화)의 이점 중 상당 부분을 이미 처리했습니다. 함수 포인터를 사용하면 상속과 같은 것을 구현하는 것이 쉽지만 솔직히 실제로는 거의 유용하지 않습니다.
typedef struct FOO_type FOO_type
헤더에 무효로 형식 정의 대신 여전히 구조를 노출하지 않으면 서 당신은 유형 검사의 추가 혜택을받을.
함수 포인터를 사용하여 위조 할 수 있으며 실제로 이론적으로 C ++ 프로그램을 C로 컴파일하는 것이 가능하다고 생각합니다.
그러나 패러다임을 사용하는 언어를 선택하기보다는 언어에 대한 패러다임을 강요하는 것이 거의 이치에 맞지 않습니다.
객체 지향 C를 수행 할 수 있습니다. 한국에서 생산되는 코드 유형을 보았습니다. 수년 동안 본 것 중 가장 끔찍한 괴물이었습니다 (코드를 본 작년 (2007)과 같았습니다). 그렇습니다. 그렇습니다. 사람들은 전에 그것을 해왔으며 오늘날에도 여전히 그렇게합니다. 그러나 C ++ 또는 Objective-C를 권장합니다. 둘 다 다른 패러다임으로 객체 방향을 제공하기 위해 C에서 태어난 언어입니다.
OOP 접근 방식이 해결하려는 문제보다 우수하다고 확신하는 경우 왜 OOO 이외의 언어로 OOP 접근 방식을 해결하려고합니까? 작업에 잘못된 도구를 사용하고있는 것 같습니다. C ++ 또는 다른 객체 지향 C 변형 언어를 사용하십시오.
C로 작성된 기존의 대규모 프로젝트에서 코딩을 시작하기 때문에 묻는 경우, 자신 또는 다른 사람의 OOP 패러다임을 프로젝트 인프라에 강제로 적용해서는 안됩니다. 프로젝트에 이미 존재하는 지침을 따르십시오. 일반적으로, 깨끗한 API와 격리 라이브러리와 모듈은 깨끗한 OOP- 가진 향해 먼 길을 갈 것입니다 틱 디자인.
이 모든 후, 당신은 정말 OOP C 일을 설정하는 경우, 읽기 이 (PDF)를.
그래 넌 할수있어. 사람들은 C ++ 또는 Objective-C 가 등장 하기 전에 객체 지향 C를 작성했습니다 . C ++과 Objective-C는 C에서 사용 된 일부 OO 개념을 언어의 일부로 공식화하려고 시도했습니다.
다음은 모양이 같거나 메소드 호출 인 것을 만드는 방법을 보여주는 정말 간단한 프로그램입니다 (이를 수행하는 더 좋은 방법이 있습니다. 이는 언어가 개념을 지원한다는 증거 일뿐입니다).
#include<stdio.h>
struct foobarbaz{
int one;
int two;
int three;
int (*exampleMethod)(int, int);
};
int addTwoNumbers(int a, int b){
return a+b;
}
int main()
{
// Define the function pointer
int (*pointerToFunction)(int, int) = addTwoNumbers;
// Let's make sure we can call the pointer
int test = (*pointerToFunction)(12,12);
printf ("test: %u \n", test);
// Now, define an instance of our struct
// and add some default values.
struct foobarbaz fbb;
fbb.one = 1;
fbb.two = 2;
fbb.three = 3;
// Now add a "method"
fbb.exampleMethod = addTwoNumbers;
// Try calling the method
int test2 = fbb.exampleMethod(13,36);
printf ("test2: %u \n", test2);
printf("\nDone\n");
return 0;
}
추가 할 작은 OOC 코드 :
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
나는 이것을 1 년 동안 파고 왔습니다.
GObject 시스템은 순수한 C와 함께 사용하기 어렵 기 때문에 C로 OO 스타일을 쉽게하기 위해 멋진 매크로를 작성하려고했습니다.
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
여기 내 프로젝트 사이트가 있습니다 (문서를 작성할 시간이 충분하지 않습니다. 그러나 중국어 문서는 훨씬 좋습니다).
Jim Larson의 1996 년 강의에서 312 절의 점심 시간 세미나 프로그래밍 : High and Low-Level C 에서 C를 사용한 상속의 예가 있습니다 .
C에서 OOP 개념을 사용하기에 적합한 기사 나 서적은 무엇입니까?
Dave Hanson의 C 인터페이스 및 구현 은 캡슐화 및 이름 지정 기능 이 뛰어나고 함수 포인터 사용 기능 이 우수 합니다. Dave는 상속을 시뮬레이션하지 않습니다.
X Window 용 Xt 툴킷 의 구현을 살펴 보는 것이 좋습니다 . 물론 치아가 오래 걸리지 만 사용 된 많은 구조는 전통적인 C 내에서 OO 방식으로 작동하도록 설계되었습니다. 일반적으로 이것은 여기 저기에 간접 레이어를 추가하고 서로 겹쳐서 구조를 설계하는 것을 의미합니다.
OO와 같은 방식으로 C에 위치한 OO 방식으로 많은 것을 할 수 있습니다 #include<favorite_OO_Guru.h>
. 그들은 실제로 당시의 많은 모범 사례를 구성했습니다. OO 언어와 시스템은 오늘날의 프로그래밍 자이 거스트의 일부만 증류하고 증폭했습니다.
질문에 대한 대답은 '예, 가능합니다'입니다.
객체 지향 C (OOC) 키트는 객체 지향 방식으로 프로그래밍하려는 사람을위한 것이지만 오래된 C에도 적용됩니다. OOC는 클래스, 단일 및 다중 상속, 예외 처리를 구현합니다.
풍모
• 언어 확장이 필요없는 C 매크로와 함수 만 사용하십시오! (ANSI-C)
• 어플리케이션을위한 읽기 쉬운 소스 코드. 가능한 한 간단하게하기 위해주의를 기울였습니다.
• 클래스의 단일 상속
인터페이스와 믹스 인에 의한 다중 상속 (버전 1.3부터)
• 예외 구현 (순 C!)
• 클래스를위한 가상 함수
• 쉬운 수업 구현을위한 외부 도구
자세한 내용은 http://ooc-coding.sourceforge.net/을 방문하십시오 .
사람들이 C를 사용하여 C ++ 스타일을 에뮬레이트하려고하는 것처럼 보입니다. 객체 지향 프로그래밍을 수행하는 것은 C가 실제로 구조체 지향 프로그래밍을 수행한다는 것입니다. 그러나 늦게 바인딩, 캡슐화 및 상속 같은 것을 얻을 수 있습니다. 상속을 위해 하위 구조체에서 기본 구조체에 대한 포인터를 명시 적으로 정의하면 분명히 다중 상속 형태입니다. 또한 귀하의
//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);
//private_class.c
struct inherited_class_1;
struct inherited_class_2;
struct private_class {
int a;
int b;
struct inherited_class_1 *p1;
struct inherited_class_2 *p2;
};
struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();
struct private_class * new_private_class() {
struct private_class *p;
p = (struct private_class*) malloc(sizeof(struct private_class));
p->a = 0;
p->b = 0;
p->p1 = new_inherited_class_1();
p->p2 = new_inherited_class_2();
return p;
}
int ret_a_value(struct private_class *p, int a, int b) {
return p->a + p->b + a + b;
}
void delete_private_class(struct private_class *p) {
//release any resources
//call delete methods for inherited classes
free(p);
}
//main.c
struct private_class *p;
p = new_private_class();
late_bind_function = &implementation_function;
delete_private_class(p);
로 컴파일하십시오 c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.
따라서 조언은 순수한 C 스타일을 고수하고 C ++ 스타일을 강요하지 않는 것입니다. 또한이 방법은 API를 작성하는 매우 깨끗한 방법입니다.
C의 OOP에 대한 또 다른 변형 은 http://slkpg.byethost7.com/instance.html 을 참조 하십시오 . 네이티브 C 만 사용하여 재진입을위한 인스턴스 데이터를 강조합니다. 다중 상속은 함수 래퍼를 사용하여 수동으로 수행됩니다. 타입 안전이 유지됩니다. 다음은 작은 샘플입니다.
typedef struct _peeker
{
log_t *log;
symbols_t *sym;
scanner_t scan; // inherited instance
peek_t pk;
int trace;
void (*push) ( SELF *d, symbol_t *symbol );
short (*peek) ( SELF *d, int level );
short (*get) ( SELF *d );
int (*get_line_number) ( SELF *d );
} peeker_t, SlkToken;
#define push(self,a) (*self).push(self, a)
#define peek(self,a) (*self).peek(self, a)
#define get(self) (*self).get(self)
#define get_line_number(self) (*self).get_line_number(self)
INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
return d->scan.line_number;
}
PUBLIC
void
InitializePeeker ( peeker_t *peeker,
int trace,
symbols_t *symbols,
log_t *log,
list_t *list )
{
InitializeScanner ( &peeker->scan, trace, symbols, log, list );
peeker->log = log;
peeker->sym = symbols;
peeker->pk.current = peeker->pk.buffer;
peeker->pk.count = 0;
peeker->trace = trace;
peeker->get_line_number = get_line_number;
peeker->push = push;
peeker->get = get;
peeker->peek = peek;
}
나는 파티에 조금 늦었지만 주제에 대한 경험을 나누고 싶다 : 요즘 임베디드 것들로 작업하고 있고 내가 가지고있는 유일한 (신뢰할 수있는) 컴파일러는 C이므로 객체 지향을 적용하고 싶다 C로 작성된 내 임베디드 프로젝트의 접근 방식
지금까지 내가 본 대부분의 솔루션은 타입 캐스트를 많이 사용하므로 타입 안전성을 잃어 버립니다. 실패하면 컴파일러가 도움이되지 않습니다. 이것은 완전히 받아 들일 수 없습니다.
내가 가진 요구 사항 :
이 기사에서 내 접근 방식에 대해 자세히 설명했습니다 . C의 객체 지향 프로그래밍 ; 또한 기본 클래스와 파생 클래스에 대한 상용구 코드 자동 생성 유틸리티가 있습니다.
나는 그것을 시도한 작은 라이브러리를 만들었고 나에게 정말 훌륭하게 작동합니다. 그래서 나는 경험을 공유한다고 생각했다.
https://github.com/thomasfuhringer/oxygen
단일 상속은 구조체를 사용하여 매우 쉽게 구현하고 다른 모든 자식 클래스에 대해 확장 할 수 있습니다. 부모 구조로 간단히 캐스트하면 모든 자손에서 부모 메서드를 사용할 수 있습니다. 변수가 이런 종류의 객체를 보유하는 구조체를 가리키는 한, 항상 루트 클래스로 캐스팅하고 내성을 수행 할 수 있습니다.
언급했듯이 가상 방법은 다소 까다 롭습니다. 그러나 그들은 할 수 있습니다. 일을 단순하게 유지하기 위해 클래스 설명 구조에 함수 배열을 사용하면 모든 자식 클래스가 필요한 경우 개별 슬롯을 복사하고 다시 채 웁니다.
다중 상속은 구현하기가 다소 복잡하며 성능에 상당한 영향을 미칩니다. 그래서 나는 떠납니다. 필자는 실제 상황을 깨끗하게 모델링하는 것이 바람직하고 유용한 경우를 고려하지만, 90 %의 경우 단일 상속이 요구를 충족시킵니다. 그리고 단일 상속은 간단하고 비용이 들지 않습니다.
또한 타입 안전에 관심이 없습니다. 프로그래밍 실수를 막기 위해 컴파일러에 의존해서는 안된다고 생각합니다. 그리고 어쨌든 약간의 오류로부터 당신을 보호합니다.
일반적으로 객체 지향 환경에서는 메모리 관리를 가능한 한 자동화하기 위해 참조 카운팅을 구현하려고합니다. 또한“개체”루트 클래스에 참조 카운트를 추가하고 힙 메모리의 할당 및 할당 해제를 캡슐화하는 일부 기능을 제공합니다.
그것은 모두 매우 간단하고 간결하며 C ++ 인 괴물을 다루지 않고도 OO의 필수 요소를 제공합니다. 저는 C 랜드에 머무르는 유연성을 유지하며, 이는 특히 타사 라이브러리를 쉽게 통합 할 수있게합니다.
C의 수퍼 세트 인 Objective-C를 사용할 것을 제안합니다.
Objective-C는 30 세이지 만 우아한 코드를 작성할 수 있습니다.
예, 그러나 C로 어떤 종류의 다형성을 구현하려는 사람은 없었습니다.