source

C의 객체 지향 프로그래밍

gigabyte 2022. 8. 18. 23:47
반응형

C의 객체 지향 프로그래밍

중복 가능성:
오브젝트 지향 코드를 C로 쓸 수 있나요?
객체 지향 패턴(C)

얼마 전 누군가(Linus Torvalds였던 것 같다)가 C++가 얼마나 끔찍한 언어인지, C로 객체 지향 프로그램을 작성하는 방법에 대해 이야기한 것을 읽은 기억이 난다.곰곰이 생각해 본 결과, 모든 오브젝트 지향의 개념이 C로 어떻게 계승되는지 잘 모르겠습니다.어떤 것들은 꽤 명백하다.예를 들어 다음과 같습니다.

  1. 멤버 함수를 에뮬레이트하기 위해 함수 포인터를 구조체에 넣을 수 있습니다.
  2. 다형성을 에뮬레이트하기 위해 예를 들어 다음과 같이 다양한 수의 인수를 사용하고 부두(voodoo)를 실행하는 함수를 작성할 수 있습니다.sizeof파라미터

그러나 캡슐화와 상속은 어떻게 에뮬레이트합니까?

캡슐화는 개인 구성원을 저장하는 중첩된 구조를 갖는 것으로 모방할 수 있을 것 같습니다.이동은 꽤 쉽겠지만, 아마도 이름을 붙일 수 있을 것이다.PRIVATE아니면 건물 밖에서 사용하는 것이 아니라는 신호를 보낼 수 있을 만큼 명백한 것이거나요하지만 유산은?

일반 함수 및 가상 테이블(vtable)을 사용하여 다형성을 구현할 수 있습니다.여기 프로그래밍 연습을 위해 제가 (C++를 기반으로) 개발한 꽤 깔끔한 시스템이 있습니다.alt text

컨스트럭터는 메모리를 할당한 후 메모리가 초기화되는 클래스의 init 함수를 호출합니다.각 init 함수는 가상 함수 포인터를 포함하는 정적 vtable 구조도 포함해야 합니다(순수한 가상의 경우 NULL).파생 클래스 초기화 함수는 다른 작업을 수행하기 전에 슈퍼 클래스 초기화 함수를 호출합니다.

가상 함수 래퍼(vtables가 가리키는 함수와 혼동하지 말 것)를 구현하면 다음과 같이 매우 좋은 API를 만들 수 있습니다(추가).static inline헤더에 이렇게 하면 다음과 같이 됩니다.

int playerGuess(Player* this) { return this->vtable->guess(this); }

단일 상속은 구조의 바이너리 레이아웃을 남용하여 수행할 수 있습니다.alt text

계층 유형 간에 캐스팅할 때 포인터 값을 조정해야 하는 경우가 많기 때문에 다중 상속이 더 복잡합니다.

다른 유형별 데이터도 가상 테이블에 추가할 수 있습니다.예를 들어 런타임 유형 정보(예: 문자열로서의 유형 이름), 슈퍼클래스 vtable 및 소멸자 체인에 대한 링크 등이 있습니다.파생 클래스 디스트럭터가 오브젝트를 슈퍼 클래스로 강등시킨 후 베이스 클래스 디스트럭터에 도달하여 최종적으로 구조를 해방할 때까지 디스트럭터를 재귀적으로 호출하는 가상 디스트럭터가 필요할 수 있습니다.

캡슐화는 player_protected.h에서 구조체를 정의하고 player_protected.c에서 함수(vtable이 가리키는 것)를 구현함으로써 이루어졌지만, 이는 매우 서툴고 (가상 래퍼를 헤더에 넣을 수 없기 때문에) 성능을 저하시킵니다.

그 주제에 대한 "bible"을 읽어보셨나요?"개체 지향 C..."를 참조하십시오.

그러나 캡슐화와 상속은 어떻게 에뮬레이트합니까?

사실 캡슐화가 가장 쉬운 부분이에요.캡슐화는 설계 철학이며 언어 및 문제에 대한 생각과는 전혀 관계가 없습니다.

예를 들어 Windows FILE api는 완전히 캡슐화되어 있습니다.파일을 열면 파일 'object'에 대한 모든 상태 정보가 들어 있는 불투명한 개체가 반환됩니다.이 핸들을 각 파일에 다시 전달합니다.캡슐화는 실제로 C++보다 훨씬 우수합니다.이는 사용자가 개인 변수의 이름을 확인하고 확인할 수 있는 공용 헤더 파일이 없기 때문입니다.

상속은 더 어렵지만 코드가 객체 지향적이기 위해서는 전혀 필요하지 않습니다.어떤 면에서는 집약이 상속보다 더 낫고, 집약이 C++와 마찬가지로 C에서도 쉽습니다.예를 들어, 이것을 참조해 주세요.

닐에 대한 답변으로 왜 다형성을 위해 상속이 필요하지 않은지에 대한 설명은 위키피디아를 참조하십시오.

옛날 사람들은 C++ 컴파일러가 나오기 몇 년 전에 객체 지향 코드를 작성했습니다.이것은 도구 세트가 아니라 사고방식입니다.

Apple의 C 기반 CoreFoundation 프레임워크는 실제로 "객체"가 실제 OO 언어인 Objective-C로 객체 역할을 할 수 있도록 작성되었습니다.프레임워크의 꽤 큰 서브셋은 CF-Lite로서 애플 사이트의 오픈 소스입니다.주요 OS 레벨 프레임워크에서 이 방법을 사용하면 유용한 도입 사례가 될 수 있습니다.

조금 더 높은 고도에서 OOP 주류가 제안하는 것보다 문제를 좀 더 개방적으로 고려하는 객체 지향 프로그래밍은 관련 기능을 가진 데이터로서 객체를 생각하는 것을 의미합니다.이는 함수가 OOP의 패러다임을 지원하는 일반적인 언어(예: C++)에서와 같이 개체에 물리적으로 연결되어 있어야 한다는 것을 의미하지는 않습니다.

struct T
{
   int data;
   int get_data() const { return data; }
};

GTK+ 오브젝트타입 시스템에 대해 자세히 살펴보는 것이 좋습니다.이는 C 프로그래밍 언어로 구현된 OOP의 훌륭한 예입니다.

GTK+는 독자적인 커스텀 오브젝트 시스템을 구현하여 상속이나 가상 기능 등의 표준 오브젝트 지향 기능을 제공합니다.

협회는 또한 계약적이고 관례적일 수 있다.

캡슐화 및 데이터 숨김 기법에 대해서는 일반적이고 간단한 것이 불투명 포인터(또는 불투명 데이터 유형)일 수 있습니다. 정보를 로드하거나 저장하려면 불투명 포인터 뒤에 숨겨진 객체와 통신하는 방법을 알고 있는 관련 함수를 호출해야 합니다.

유사하지만 다른 하나는 섀도 데이터 유형입니다. Jon Jagger가 이 잘 알려지지 않은 기술에 대해 훌륭한 설명을 하는 링크를 확인하십시오.

gtk 및 glib 라이브러리는 매크로를 사용하여 객체를 다양한 유형으로 캐스팅합니다.
add_widget(GTK_WIDGET(myButton));
어떻게 된 건지 말할 수는 없지만 정확한 방법을 알기 위해 그들의 출처를 읽을 수 있습니다.

상속 패턴의 예에서는 Linux 커널에서 VFS 계층이 작동하는 방식을 살펴봅니다.다양한 파일 시스템의 파일 조작은 일련의 일반적인 파일 조작 기능을 "계승"합니다(예:generic_file_aio_read(),generic_file_llseek()(예: 자체 구현으로 덮어쓸 수 있습니다). ntfs_file_aio_write()).

Objective-C를 확실히 보세요.

typedef struct objc_object {
    Class isa;
} *id;

typedef struct objc_class {
    struct objc_class *isa;
    struct objc_class *super_class
    const char *name;
    long version;
    long info
    long instance_size;
    struct objc_ivar_list *ivars;
    struct objc_method_list **methodLists;
   struct objc_cache *cache;
   struct objc_protocol_list *protocols;
} *Class;

보시다시피 상속 정보는 다른 세부 정보와 함께 클래스 구조로 유지됩니다(클래스를 개체로 취급할 수도 있습니다).

Objective-C는 변수를 공개적으로 선언해야 한다는 점에서 캡슐화를 사용하는 C++와 동일한 방식으로 처리됩니다.스트레이트 C는 모듈만 내부 액세스 가능한 보이드포인터를 반환할 수 있다는 점에서 훨씬 유연합니다.따라서 캡슐화가 훨씬 좋습니다.

저는 그래픽 코스의 일환으로 기본적인 OO 스타일 C 페인트 프로그램을 작성한 적이 있습니다.클래스 선언까지는 하지 않고 vtable 포인터를 구조의 첫 번째 요소로 사용하여 수작업으로 상속을 구현했습니다.이렇게 낮은 레벨에서 vtables를 가지고 노는 것의 좋은 점은 몇 가지 포인터를 변경하여 실행 시 클래스 동작을 변경하거나 오브젝트 클래스를 동적으로 변경할 수 있다는 것입니다.모든 종류의 하이브리드 오브젝트 작성, 다중 상속 위장 등이 매우 간단했습니다.

Objective-C에 관한 훌륭한 기사 및 토론은 이쪽에서 보실 수 있습니다.

http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html

C의 객체 지향 프로그래밍의 좋은 예로는 몇 년 전의 POV-Ray 소스를 참조하십시오.버전 3.1g은 특히 좋습니다.물론 "개체"는 함수 포인터가 있는 구조체였습니다.매크로는 추상 객체의 핵심 메서드와 데이터를 제공하기 위해 사용되었으며 파생 클래스는 해당 매크로로 시작하는 구조체입니다.그러나 민간/공공 부문과 거래하려는 시도는 없었다.표시되는 것은 .h 파일, 구현의 상세 내용은 .c 파일로 되어 있습니다.다만, 많은 예외를 제외하고, 대부분은 .c 파일입니다.

함수 포인터를 재할당하는 것만으로 하나의 클래스를 다른 클래스이지만 비슷한 클래스로 즉시 변환하는 등 C++로 넘어갈 수 없는 멋진 요령이 몇 가지 있었습니다.오늘날의 역동적인 언어에 적합합니다.자세한 것은 잊어버렸습니다.CSG의 교차점과 유니언 오브젝트일 가능성이 있다고 생각합니다.

http://www.povray.org/

흥미로운 역사죠Cfront, 원래 C++ 구현 출력 C 코드, 그리고 실제로 최종 코드를 작성하려면 C 컴파일러가 필요합니다.그래서 C++로 표현할 수 있는 것은 C로 쓸 수 있다.

상속을 처리하는 한 가지 방법은 중첩된 구조를 갖는 것입니다.

struct base
{
    ...
};

void method_of_base(base *b, ...);

struct child
{
    struct base base_elements;
    ...
};

다음에, 다음과 같은 콜을 실행할 수 있습니다.

struct child c;
method_of_base(&c.b, ...);

Objective-C는 거의 그런 기능을 합니다.Objective-C OO 코드를 C로 컴파일하는 프론트 엔드일 뿐입니다.

언급URL : https://stackoverflow.com/questions/2181079/object-oriented-programming-in-c

반응형