programing

어떻게 하면 C에서 OO 스타일의 다형을 시뮬레이션 할 수 있나요?

sourcetip 2022. 8. 15. 09:54
반응형

어떻게 하면 C에서 OO 스타일의 다형을 시뮬레이션 할 수 있나요?

OO에 와 같은 쓸수 있는 요?C로그???


다음 항목도 참조하십시오.

"[c] OO"에서 검색하여 찾았습니다.

첫 번째 C++ 컴파일러("C with classes")는 실제로 C 코드를 생성하기 때문에, 그것은 확실히 실행 가능합니다.

기본적으로 기본 클래스는 구조체입니다.파생된 구조체는 첫 번째 위치에 기본 구조를 포함해야 합니다.따라서 "파생된" 구조체에 대한 포인터도 기본 구조에 대한 유효한 포인터가 됩니다.

typedef struct {
   data member_x;
} base;

typedef struct {
   struct base;
   data member_y;
} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

함수는 함수 포인터로서 구조의 일부가 될 수 있기 때문에 p->call(p) 등의 구문이 가능하지만, 여전히 명시적으로 구조체에 대한 포인터를 함수 자체에 전달해야 합니다.

일반적인 접근법은 함수에 대한 포인터로 구조를 정의하는 것입니다.이것에 의해, 임의의 타입으로 호출할 수 있는 「메서드」가 정의됩니다.그런 다음 하위 유형은 이 공통 구조에서 고유한 함수를 설정하고 반환합니다.

예를 들어 Linux 커널에는 다음과 같은 구조가 있습니다.

struct inode_operations {
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
                               struct nameidata *);
    ...
};

된 파일 의 각 은 파일 시스템의 합니다.create,lookup및 기능 , , , , , , , , , , , , , , , , , , , , , , , , , , .inode_operations odeodeodeodeode 。

struct inode_operations   *i_op;
i_op -> create(...);

C++는 C에서 그리 멀지 않다.

클래스는 VTable이라는 함수 포인터 테이블에 대한 숨겨진 포인터를 가진 구조체입니다.Vtable 자체는 정적입니다.유형이 같은 구조의 Vtables를 가리키지만 포인터가 다른 구현을 가리키면 다형성이 됩니다.

코드 혼란을 피하기 위해 구조를 파라미터로 하는 콜로직을 함수로 캡슐화할 것을 권장합니다.

또한 함수(C++ 컨스트럭터와 동일) 및 삭제(C++ 디스트럭터)에서 캡슐화 구조 인스턴스화와 초기화도 수행해야 합니다.어쨌든 이것들은 좋은 관행이다.

typedef struct
{
   int (*SomeFunction)(TheClass* this, int i);
   void (*OtherFunction)(TheClass* this, char* c);
} VTable;

typedef struct
{
   VTable* pVTable;
   int member;

} TheClass;

메서드를 호출하려면:

int CallSomeFunction(TheClass* this, int i)
{
  (this->pVTable->SomeFunction)(this, i);
}

나는 모든 엘스의 대답을 보고 이렇게 생각해냈다.

#include <stdio.h>

typedef struct
{
    int (*get)(void* this);
    void (*set)(void* this, int i);
    int member;

} TheClass;

int Get(void* this)
{
    TheClass* This = (TheClass*)this;
    return This->member;
}

void Set(void* this, int i)
{
    TheClass* This = (TheClass*)this;
    This->member = i;
}

void init(TheClass* this)
{
    this->get = &Get;
    this->set = &Set;
}

int main(int argc, char **argv)
{
    TheClass name;
    init(&name);
    (name.set)(&name, 10);
    printf("%d\n", (name.get)(&name));
    return 0;
}

그게 몇 가지 질문에 답해주길 바란다.

내가 주로 하는 일은 래핑된 클래스에 대한 메타 정보를 포함하는 다른 구조로 구조체를 래핑하고 일반 구조체에 작용하는 함수 목록과 같은 방문자 목록을 작성하는 것입니다.이 접근법의 장점은 기존 구조를 수정할 필요가 없으며 구조의 하위 집합에 대해 방문자를 작성할 수 있다는 것입니다.

일반적인 예를 들어 보겠습니다.

typedef struct {
    char call[7] = "MIAO!\n";
} Cat;
    
typedef struct {
    char call[6] = "BAU!\n";
} Dog;

이 새로운 구조로 2개의 스트럿을 감쌀 수 있습니다.

typedef struct {
    const void * animal;
    AnimalType type;
} Animal;

유형은 단순한 int일 수 있지만 게으름 피우지 말자.

typedef enum  {
    ANIMAL_CAT = 0,
    ANIMAL_DOG,
    ANIMAL_COUNT
} AnimalType;

포장 기능이 있으면 좋을 것 같습니다.

Animal catAsAnimal(const Cat * c) {
    return (Animal){(void *)c, ANIMAL_CAT};
}

Animal dogAsAnimal(const Dog * d) {
    return (Animal){(void *)d, ANIMAL_DOG};
}

이제 "방문자"를 정의할 수 있습니다.

void catCall ( Animal a ) {
    Cat * c = (Cat *)a.animal;
    printf(c->call);
}

void dogCall ( Animal a ) {
    Dog * d = (Dog *)a.animal;
    printf(d->call);
}

void (*animalCalls[ANIMAL_COUNT])(Animal)={&catCall, &dogCall};

실제 사용법은 다음과 같습니다.

Cat cat;
Dog dog;

Animal animals[2];
animals[0] = catAsAnimal(&cat);
animals[1] = dogAsAnimal(&dog);

for (int i = 0; i < 2; i++) {
    Animal a = animals[i];
    animalCalls[a.type](a);
}

이 접근법의 단점은 일반 유형으로 사용할 때마다 구조를 줄바꿈해야 한다는 것입니다.

#include <stdio.h>

typedef struct {
    int  x;
    int z;
} base;

typedef struct {
    base;
    int y;
    int x;
} derived;

void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
    printf("Class base [%d]\n",a->x);
    printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
    printf("Class derived [%d]\n",b->y);
    printf("Class derived [%d]\n",b->x);
}

int main()
{
    derived d;
    base b;
    printf("Teste de poliformismo\n");

    b.x = 2;
    d.y = 1;
    b.z = 3;
    d.x = 4;
    function_on_base(&b);
    function_on_base(&d);
    function_on_derived(&b);
    function_on_derived(&d);
    return 0;
}

출력은 다음과 같습니다.

Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]

다형성 코드인 셈이죠

처음에 Zeiv 아저씨가 설명해 주셨어요.

VPRI의 Ian Piumarta와 Alessandro Warth에 의한 기사 Open Reusable Object Models의 부록 B는 GNU C에서 오브젝트 모델을 구현한 것으로 약 140줄의 코드입니다.그것은 매우 흥미로운 읽을거리이다!

오브젝트에 메시지를 송신하는 캐시되지 않은 매크로의 버전을 다음에 나타냅니다.C에 대한 GNU 확장자를 사용합니다(스테이트먼트 표현).

struct object;

typedef struct object *oop; 
typedef oop *(*method_t)(oop receiver, ...);

//...

#define send(RCV, MSG, ARGS...) ({ \ 
    oop r = (oop)(RCV); \ 
    method_t method = _bind(r, (MSG)); \ 
    method(r, ##ARGS); \ 
}) 

같은 문서 내에서object,vtable,vtable_delegated그리고.symbol구조 및_bind그리고.vtable_lookup기능들.

건배!

파일 함수 fopen, fclose, fread는 C의 OO 코드의 예입니다.클래스 내의 개인 데이터 대신 데이터를 캡슐화하는 데 사용되는 FILE 구조에서 작업하고 C 함수는 멤버 클래스 함수로 작동합니다.http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

Wikipedia에서:프로그래밍 언어와 유형 이론에서 다형성(polymorphism)은 다른 유형의 실체에 단일 인터페이스를 제공하는 것이다.

따라서 C에 실장하는 유일한 방법은 variadic 인수를 사용하는 것 외에 몇 가지 (반)자동 타입의 정보 관리를 사용하는 것입니다.예를 들어, C++ 에서는 다음과 같이 쓸 수 있습니다(사소한 것에 대해서는 죄송합니다).

void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );

C에서는, 다른 솔루션 중에서도, 최선은 다음과 같습니다.

int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );

void add( int typeinfo, void* result, ... );

다음 사항이 필요합니다.

  1. "typeinfo"를 enums/info로 구현하다
  2. stdarg.h를 사용하여 후자의 기능을 구현하다
  3. C 정적 유형 확인과 작별하다

나는 다른 다형성 구현이 바로 이것과 매우 비슷하다고 거의 확신한다.위의 답변은 다형성보다 상속을 더 많이 다루고 있는 것 같습니다!

또한 C에 OO기능을 구축하기 위해 이전 답변을 볼 수 있습니다.

단, (다른 질문에서도 이 질문으로 리다이렉트 된 것처럼) C언어의 예를 들어 다형성이 무엇인지 알고 싶은 경우.내가 틀렸을지도 모르지만, C 포인터 산술만큼 이해하기 쉬운 것은 생각할 수 없다.내 생각에 포인터 산수는 본질적으로 C에서 다형이다.다음 예제에서는 동일한 함수(OO의 방법), 즉 더하기(+)가 입력 구조의 속성에 따라 다른 동작을 생성합니다.

예:

double a*;
char str*;

a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char)); 

a=a+2; // make the pointer a, point 2*8 bytes ahead.

str=str+2; // make the pointer str, point 2*1 bytes ahead.

면책사항:저는 C에 처음 온 사람이고, 다른 사용자의 코멘트에서 수정하고 배우기를 고대하고 있습니다.잘못되면 이 답변을 완전히 지웁니다.대단히 고맙습니다,

단순한 함수 오버로드의 매우 조잡한 예로서, 다양한 매크로를 사용하여 많은 것을 달성할 수 있습니다.

#include <stdio.h>
#include <stdlib.h>

#define SCOPE_EXIT(X) __attribute__((cleanup (X)))

struct A
{
  int a;
};

struct B
{
 int a, b;
};

typedef struct A * A_id;
typedef struct B * B_id;


A_id make_A()
{
   return (A_id)malloc(sizeof(struct A));
}

void destroy_A(A_id * ptr)
{
   free(*ptr);
   *ptr = 0;
}

B_id make_B()
{
  return (B_id)malloc(sizeof(struct B));
}

void destroy_B(B_id * ptr)
{
  free(*ptr);
  *ptr = 0;
}

void print_a(A_id ptr)
{
  printf("print_a\n"); 
}
void print_b(B_id ptr)
{
  printf("print_b\n"); 
}

#define print(X) _Generic((X),\
          A_id : print_a, \
          B_id : print_b\
)(X)

int main()
{
  A_id aa SCOPE_EXIT(destroy_A) = make_A();
  print(aa);

  B_id bb SCOPE_EXIT(destroy_B) = make_B();
  print(bb);
  return 0;
}

언급URL : https://stackoverflow.com/questions/524033/how-can-i-simulate-oo-style-polymorphism-in-c

반응형