C에서 클래스를 구현하려면 어떻게 해야 합니까?
C(C++ 컴파일러 또는 객체 지향 컴파일러 없음)를 사용해야 하며 동적 메모리 할당이 없다고 가정할 때 클래스 또는 클래스 근사 구현에 사용할 수 있는 기술은 무엇입니까?클래스는 항상 다른 파일로 분리하는 것이 좋은가요?메모리를 미리 할당하려면 일정한 인스턴스 수를 상정하거나 컴파일 시간 전에 각 객체에 대한 참조를 상수로 정의합니다.어떤 OOP 개념을 구현해야 하는지 가정하고(각각 다를 수 있음), 각각의 최적의 방법을 제안해 주십시오.
제약사항:
- 임베디드 시스템용 코드를 쓰고 있는데 컴파일러와 기존 코드 베이스가 C에 있기 때문에 OOP가 아닌 C를 사용해야 합니다.
- 동적 메모리 할당은 없습니다.이는 동적 할당을 시작해도 메모리가 부족하지 않다고 합리적으로 가정할 수 있기 때문입니다.
- 사용하는 컴파일러는 함수 포인터에 문제가 없습니다.
이는 원하는 정확한 "개체 지향" 기능 세트에 따라 달라집니다.오버로드 및/또는 가상 메서드 같은 것이 필요한 경우 구조에 함수 포인터를 포함할 필요가 있습니다.
typedef struct {
float (*computeArea)(const ShapeClass *shape);
} ShapeClass;
float shape_computeArea(const ShapeClass *shape)
{
return shape->computeArea(shape);
}
이를 통해 기본 클래스를 "상속"하고 적절한 함수를 구현하여 클래스를 구현할 수 있습니다.
typedef struct {
ShapeClass shape;
float width, height;
} RectangleClass;
static float rectangle_computeArea(const ShapeClass *shape)
{
const RectangleClass *rect = (const RectangleClass *) shape;
return rect->width * rect->height;
}
물론 함수 포인터가 올바르게 설정되어 있는지 확인하기 위해 생성자도 구현해야 합니다.통상은 인스턴스에 메모리를 동적으로 할당하지만, 발신자에게도 할당할 수 있습니다.
void rectangle_new(RectangleClass *rect)
{
rect->width = rect->height = 0.f;
rect->shape.computeArea = rectangle_computeArea;
}
의 서로 생성자를 함수 . 여러 의 생성자를 수 여러 개 사용할 수 없습니다.rectangle_new()
★★★★
void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
rectangle_new(rect);
rect->width = width;
rect->height = height;
}
다음은 사용 예를 보여 주는 기본적인 예입니다.
int main(void)
{
RectangleClass r1;
rectangle_new_with_lengths(&r1, 4.f, 5.f);
printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
return 0;
}
적어도 이게 너에게 아이디어를 주길 바라.C에서 성공적이고 풍부한 객체 지향 프레임워크에 대해서는 glib의 GObject 라이브러리를 조사합니다.
상기의 「클래스에는 명시적인 「클래스」는 C에서 볼 수 있는 더 한 메서드 .각 오브젝트에는 C++에서 일반적으로 볼 수 있는 것보다 조금 더 유연한 메서드 포인터가 있습니다.또한 메모리 비용도 많이 듭니다.를 '마음껏'에 수 요.class
각 오브젝트 인스턴스에 클래스를 참조하는 방법을 개발합니다.
나도 숙제 때문에 한 번 해야 했어.저는 다음 접근방식을 따랐습니다.
- 구조체의 데이터 구성원을 정의합니다.
- 첫 번째 인수로 구조에 포인터를 가져오는 함수 멤버를 정의합니다.
- 1개의 헤더와1개의 c로 실행합니다.구조 정의 및 함수 선언의 헤더, 구현의 경우 c.
간단한 예는 다음과 같습니다.
/// Queue.h
struct Queue
{
/// members
}
typedef struct Queue Queue;
void push(Queue* q, int element);
void pop(Queue* q);
// etc.
///
OOP가 C에서 어떻게 이루어져야 하는지 간단한 예를 제시하겠습니다.나는 이 광고가 2009년부터라는 것을 알지만 어쨌든 이것을 추가하고 싶다.
/// Object.h
typedef struct Object {
uuid_t uuid;
} Object;
int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);
/// Person.h
typedef struct Person {
Object obj;
char *name;
} Person;
int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);
/// Object.c
#include "object.h"
int Object_init(Object *self)
{
self->uuid = uuid_new();
return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
return self->uuid;
}
int Object_clean(Object *self)
{
uuid_free(self->uuid);
return 0;
}
/// Person.c
#include "person.h"
int Person_init(Person *self, char *name)
{
Object_init(&self->obj); // Or just Object_init(&self);
self->name = strdup(name);
return 0;
}
int Person_greet(Person *self)
{
printf("Hello, %s", self->name);
return 0;
}
int Person_clean(Person *self)
{
free(self->name);
Object_clean(self);
return 0;
}
/// main.c
int main(void)
{
Person p;
Person_init(&p, "John");
Person_greet(&p);
Object_get_uuid(&p); // Inherited function
Person_clean(&p);
return 0;
}
기본 개념은 '상속 클래스'를 구조물의 맨 위에 배치하는 것입니다.이와 같이 구조체의 처음 4바이트에 액세스하면 '상속 클래스'의 처음 4바이트에도 액세스합니다(비최적화를 가정).이제 구조의 포인터가 '상속되는 클래스'에 캐스팅되면 '상속되는 클래스'는 정상적으로 멤버에 액세스하는 것과 같은 방법으로 '상속되는 값'에 액세스할 수 있습니다.
이것과 컨스트럭터, 디스트럭터, 할당 및 할당 해제 함수(init, clean, new, free를 권장합니다)의 명명 규칙을 사용하면 큰 도움이 됩니다.
가상 함수에 대해서는 Class_func(...); wrapper와 함께 구조체의 함수 포인터를 사용합니다.(단순한) 템플릿은 size_t 파라미터를 추가하여 크기를 결정하거나 void* 포인터를 요구하거나 원하는 기능만을 갖춘 클래스 타입을 요구합니다.(예: int GetUUID(개체 * 자체), GetUUID(&p);)
GTK는 완전히 C를 기반으로 구축되었으며 많은 OOP 개념을 사용합니다.GTK의 소스코드를 읽었는데, 꽤 인상적이고 읽기 쉬워요.기본 개념은 각 "클래스"가 단순한 구조이며 연관된 정적 함수라는 것입니다.정적 함수는 모두 "instance" 구조를 매개 변수로 받아들여 필요한 작업을 수행하고 필요에 따라 결과를 반환합니다.예를 들어, "GetPosition(CircleStruct obj)" 함수를 사용할 수 있습니다.이 함수는 단순히 구조체를 파헤쳐 위치 번호를 추출하고 새로운 PositionStruct 객체를 구축하여 새로운 PositionStruct에 x와 y를 고정하고 반환합니다.GTK는 구조체 내부에 구조체를 내장하여 상속을 구현하기도 합니다.꽤 영리하죠
C 인터페이스 및 구현: 재사용 가능한 소프트웨어를 만드는 기술, David R. 핸슨
이 책은 당신의 질문을 훌륭하게 다루고 있다.애디슨 웨슬리 프로페셔널 컴퓨터 시리즈에요
기본적인 패러다임은 다음과 같습니다.
/* for data structure foo */
FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
하나만 에는 1등급으로 합니다.struct
를 "합니다.s "member"는 "member"의 "member"의 "member"의 "member"의 "member"의 "member"의 "member"의 "member"의 "member"에 포인터를 전달합니다.하시면 됩니다.typedef struct _whatever Whatever
「」라고 선언하기 struct _whatever
클라이언트 코드로부터 실장을 숨깁니다.와 'C 표준 라이브러리'는FILE
★★★★★★ 。
상속 및 가상 함수를 포함하는 클래스를 둘 이상 원하는 경우, 함수에 대한 포인터를 구조의 구성원으로 사용하거나 가상 함수 테이블에 대한 공유 포인터를 갖는 것이 일반적입니다.GObject 라이브러리는 이 트릭과 typedef 트릭을 모두 사용하며 널리 사용됩니다.
또한 ANSI C를 사용한 객체 지향 프로그래밍이라는 온라인에서 이용할 수 있는 기술에 대한 책도 있습니다.
GOBJECT를 보실 수 있습니다.오브젝트를 상세하게 작성할 수 있는 OS 라이브러리입니다.
http://library.gnome.org/devel/gobject/stable/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>
/**
* Define Shape class
*/
typedef struct Shape Shape;
struct Shape {
/**
* Variables header...
*/
double width, height;
/**
* Functions header...
*/
double (*area)(Shape *shape);
};
/**
* Functions
*/
double calc(Shape *shape) {
return shape->width * shape->height;
}
/**
* Constructor
*/
Shape _Shape() {
Shape s;
s.width = 1;
s.height = 1;
s.area = calc;
return s;
}
/********************************************/
int main() {
Shape s1 = _Shape();
s1.width = 5.35;
s1.height = 12.5462;
printf("Hello World\n\n");
printf("User.width = %f\n", s1.width);
printf("User.height = %f\n", s1.height);
printf("User.area = %f\n\n", s1.area(&s1));
printf("Made with \xe2\x99\xa5 \n");
return 0;
};
사용하다struct
클래스의 데이터 멤버를 시뮬레이트합니다.방법 범위와 관련하여 개인 함수 프로토타입을 .c 파일에 배치하고 공용 함수를 .h 파일에 배치하여 개인 방법을 시뮬레이션할 수 있습니다.
전략은 다음과 같습니다.
- 클래스에 대한 모든 코드를 별도의 파일에 정의합니다.
- 클래스의 모든 인터페이스를 별도의 헤더 파일로 정의합니다.
- 모든 멤버 함수는 인스턴스 이름(o.foo() 대신 foo()를 나타내는 "ClassHandle"을 사용합니다.
- 생성자는 메모리 할당 전략에 따라 함수 void ClassInit(ClassHandle h, int x, int y, ...) 또는 ClassHandle ClassInit(int x, int y, ...)로 대체됩니다.
- 모든 멤버 변수는 클래스 파일에 정적 구조의 멤버로 저장되며 파일에 캡슐화되어 외부 파일이 액세스할 수 없습니다.
- 오브젝트는 위의 정적 구조의 배열에 저장되며, 사전 정의된 핸들(인터페이스에 표시됨) 또는 인스턴스화할 수 있는 오브젝트의 고정 제한이 있습니다.
- 필요에 따라 클래스에는 배열을 루프하여 인스턴스화된 모든 객체의 함수를 호출하는 퍼블릭함수가 포함될 수 있습니다(RunAll()은 각 Run(oHandle)을 호출합니다).
- Deinit(ClassHandle h) 함수는 동적 할당 전략에서 할당된 메모리(어레이 인덱스)를 해방합니다.
이 접근방식의 변화 중 문제점, 구멍, 잠재적인 함정 또는 숨겨진 이점/결손을 알고 있는 사람이 있습니까?만약 제가 디자인 방법을 재창조하고 있다면(그리고 그렇게 해야 한다고 생각합니다), 그 이름을 가르쳐 주시겠습니까?
최초의 c++ 컴파일러는 실제로 C++ 코드를 C로 변환하는 프리프로세서였습니다.
그래서 C로 수업을 듣는 것은 매우 가능합니다.오래된 C++ 프리프로세서를 파헤쳐 어떤 종류의 솔루션을 작성하는지 확인해 보십시오.
당신의 경우 클래스의 근사치는 ADT일 수 있습니다.하지만 여전히 똑같지는 않을 것이다.
가능하다.당시에는 항상 좋은 생각 같지만 나중에는 유지보수의 악몽이 된다.당신의 코드는 모든 것을 하나로 묶는 코드 조각들로 뒤죽박죽이 됩니다.새로운 프로그래머는 함수 포인터를 사용하면 어떤 함수가 호출되는지 명확하지 않기 때문에 코드를 읽고 이해하는 데 많은 문제가 있을 것입니다.
get/set 기능으로 데이터를 숨기는 것은 C에서 구현하기 쉽지만, 여기서 그칩니다.임베디드 환경에서 여러 번 시도했지만 결국 유지보수의 문제가 됩니다.
여러분 모두 준비되셨으니 제가 피하겠습니다.
은 '이행하다'를 입니다.struct
그리고 별도의 소스에 모든primarily-associated 기능 file(s)이 너무나"간편하게."사용될 수 있다.
당신의 컴파일러에 따라에 기능을 포함할 수 있을 것이다.struct
있지만 그것은 매우compiler-specific 신전이고 아무것도 나는 일상적으로 사용하는 표준:)의 마지막 버전과 관련이 있다.
당신은 가상 메서드를 원하십니까?
그렇지 않다면 그 다음 함수 포인터의 구조 그 자체로 집합을 정의하다.만약 당신이 표준 C기능에 모든 기능 pointers을 할당해 준 다음 C로부터 매우 비슷한 구문에 어떻게 해 C++에 함수를 호출할 수 있을 것이다.
가상 방식을 사용하려는 경우 더 복잡해집니다.기본적으로 각 구조체에 독자적인 VTable을 구현하고 호출되는 함수에 따라 VTable에 함수 포인터를 할당해야 합니다.그런 다음 VTable의 함수 포인터를 호출하는 구조 자체에 함수 포인터 세트가 필요합니다.이것이 본질적으로 C++가 하는 일입니다.
TBH... 후자를 원한다면 사용할 수 있는 C++ 컴파일러를 찾아서 프로젝트를 다시 컴파일하는 것이 좋습니다.C++는 임베디드에서 사용할 수 없다는 강박관념을 이해하지 못했습니다.여러 번 사용했는데 동작도 빠르고 메모리 문제도 없습니다.물론 당신이 하는 일에 조금 더 신중해야 하지만 사실 그렇게 복잡하지는 않다.
C는 지적하신 바와 같이 OOP 언어가 아니기 때문에 진정한 클래스를 작성할 수 있는 빌트인 방법은 없습니다.가장 좋은 방법은 구조 및 함수 포인터를 살펴보는 것입니다. 이러한 포인터는 클래스의 근사치를 만들 수 있습니다.다만, C는 프로시저이기 때문에, (클래스를 사용하지 않고) C와 같은 코드의 작성을 검토해 주세요.
또한 C를 사용할 수 있으면 C++를 사용하여 수업을 받을 수 있습니다.
언급URL : https://stackoverflow.com/questions/1403890/how-do-you-implement-a-class-in-c
'programing' 카테고리의 다른 글
컴포넌트에 상태가 표시되는 이유는 무엇입니까? (0) | 2022.08.29 |
---|---|
Vue 라우터 입력 전 vs 각 입력 전 (0) | 2022.08.29 |
iOS에서 더블/플로트의 최대값은 얼마입니까? (0) | 2022.08.29 |
Android 응용 프로그램에서 사용자 설정을 저장하는 가장 적절한 방법은 무엇입니까? (0) | 2022.08.29 |
Java String 배열: 메서드의 크기가 있습니까? (0) | 2022.08.29 |