programing

C의 기본 생성자

bestprogram 2023. 10. 4. 22:14

C의 기본 생성자

구조로 정의된 C 사용자 유형에 대해 일종의 기본 생성자(예: C++)를 가질 수 있는 방법이 있습니까?

와 같은 : 에)를pthread_mutex시 수 하지만 저는 여러분이 선언문에 어떤 구조의 일부(또는 모든) 필드를 채울 수 있는지 알고 싶었습니다.

를 들면 .pthread_mutex를 들어어,는을 .

pthread_mutex_t my_mutex;

와 같은 효과를 가지다

pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;

구조물에 포인터를 가져다 주는 이니셜라이저 함수를 만들 수 있습니다.이것은 일반적인 관례였습니다.

또한 구조물을 생성하고 초기화하는 기능(공장과 같이) - 구조물이 "클라이언트" 코드에서 "초기화되지 않은" 경우는 없습니다.물론이죠. 사람들이 관습을 따르고 "시공자"/공장을 사용한다고 가정할 때...

malloc 또는 free에서 오류 검사가 없는 끔찍한 의사 코드

somestruct* somestruct_factory(/* per haps some initializer agrs? */)
{
  malloc some stuff
  fill in some stuff
  return pointer to malloced stuff
}


void somestruct_destructor(somestruct*)
{
  do cleanup stuff and also free pointer
  free(somestruct);
}

누군가가 와서 초기 C++ 프리프로세서/컴파일러가 어떻게 이 모든 것을 C에서 수행했는지 설명할 것입니다.

C++는 이 경우 "클래스"가 없다는 점에서 C와 다릅니다.그러나 C(다른 여러 언어와 마찬가지로)는 객체 지향 프로그래밍에 여전히 사용될 수 있습니다.이 경우 생성자는 구조물을 초기화하는 함수가 될 수 있습니다.이는 생성자와 동일합니다(다른 구문만 있음).또 다른 차이점은 malloc()(또는 일부 변형)을 사용하여 개체를 할당해야 한다는 것입니다.C++에서는 'new' 연산자를 간단히 사용할 수 있습니다.

예: C++ 코드:

class A {
  public:
    A() { a = 0; }
    int a;
};

int main() 
{
  A b;
  A *c = new A;
  return 0;
}

등가 C 코드:

struct A {
  int a;
};

void init_A_types(struct A* t)
{
   t->a = 0;
}

int main()
{
   struct A b;
   struct A *c = malloc(sizeof(struct A));
   init_A_types(&b);
   init_A_types(c);
   return 0;
}

함수 'init_'A_type'은 C++에서 생성자로서 기능합니다.

옛날에 모범 사례로 여겨졌던 완벽한 엔지니어링 솔루션에 대해 이야기해 보겠습니다.

구조의 문제점은 모든 것이 공개되어 있어서 데이터를 숨기지 않는다는 것입니다.

우리는 그것을 고칠 수 있다.

두 개의 헤더 파일을 만듭니다.하나는 코드의 클라이언트가 사용하는 "공개" 헤더 파일입니다.여기에는 다음과 같은 정의가 포함되어 있습니다.

typedef struct t_ProcessStruct *t_ProcessHandle;

extern t_ProcessHandle NewProcess();
extern void DisposeProcess(t_ProcessHandle handle);

typedef struct t_PermissionsStruct *t_PermissionsHandle;

extern t_PermissionsHandle NewPermissions();
extern void DisposePermissions(t_PermissionsHandle handle);

extern void SetProcessPermissions(t_ProcessHandle proc, t_PermissionsHandle perm);

그런 다음 다음과 같은 정의를 포함하는 개인 헤더 파일을 만듭니다.

typedef void (*fDisposeFunction)(void *memoryBlock);

typedef struct {
    fDisposeFunction _dispose;
} t_DisposableStruct;

typedef struct {
    t_DisposableStruct_disposer; /* must be first */
    PID _pid;
    /* etc */
} t_ProcessStruct;

typedef struct {
    t_DisposableStruct_disposer; /* must be first */
    PERM_FLAGS _flags;
    /* etc */
} t_PermissionsStruct;

구현 시 다음과 같은 작업을 수행할 수 있습니다.

static void DisposeMallocBlock(void *process) { if (process) free(process); }

static void *NewMallocedDisposer(size_t size)
{
    assert(size > sizeof(t_DisposableStruct);
    t_DisposableStruct *disp = (t_DisposableStruct *)malloc(size);
    if (disp) {
       disp->_dispose = DisposeMallocBlock;
    }
    return disp;
}

static void DisposeUsingDisposer(t_DisposableStruct *ds)
{
    assert(ds);
    ds->_dispose(ds);
}

t_ProcessHandle NewProcess()
{
    t_ProcessHandle proc =  (t_ProcessHandle)NewMallocedDisposer(sizeof(t_ProcessStruct));
    if (proc) {
        proc->PID = NextPID(); /* etc */
    }
    return proc;
}

void DisposeProcess(t_ProcessHandle proc)
{
    DisposeUsingDisposer(&(proc->_disposer));
}

사용자가 공용 헤더 파일에 있는 구조물에 대해 순방향 선언을 하는 경우가 발생합니다.당신의 구조는 불투명해요 고객들이 그걸 가지고 놀 수 없다는 뜻이죠그런 다음 전체 선언에서 일반적으로 호출할 수 있는 모든 구조의 맨 앞에 파괴자를 포함합니다.모든 사람들에게 동일한 처리 기능을 사용할 수 있는 동일한 malloc allocator를 사용할 수 있습니다.노출할 요소에 대해 공개 설정/가져오기 기능을 만듭니다.

갑자기 당신의 코드가 훨씬 더 제정신이 되었습니다.할당자 또는 할당자를 호출하는 함수에서만 구조를 얻을 수 있으므로 초기화를 병목할 수 있습니다.당신은 파괴자들을 만들어 그 물체를 파괴할 수 있게 합니다.그리고 당신도 가보세요.그런데 t_DisposibleStruct보다 더 좋은 이름은 t_vTableStruct일 수도 있습니다. 왜냐하면 그런 이름이기 때문입니다.이제 모든 함수 포인터인 vTableStruct를 사용하여 가상 상속을 구축할 수 있습니다.또한 vtable의 선택 요소를 즉시 변경하는 것과 같이 순수 oo 언어(일반적으로)로 할 수 없는 작업을 할 수도 있습니다.

중요한 점은 구조물을 안전하고 초기화 가능하게 만드는 엔지니어링 패턴이 있다는 것입니다.

아니요, 직접은 아닙니다.가장 가까운 방법은 인스턴스를 할당하고 일부 필드를 채우는 함수를 작성하는 것입니다.

C 구조를 반환하는 함수를 작성할 수 있습니다.

struct file create_file(int i, float f) {
    struct file obj = { i, f };
    // other code here...
    return obj;
}

C에서 "normal" member function을 가질 수 있는지 궁금하다면,어느 정도는 할 수 있습니다.저는 우선 논법적인 목적어 스타일을 선호합니다.첫 번째 인수로 구조체에 포인터를 전달합니다.이렇게 하면 개체에 대한 인터페이스를 정의하는 여러 가지 기능을 사용할 수 있습니다.

int file_get_integer(struct file *self) { return self->i; }
float file_get_float(struct file *self) { return self->f; }

그런 식으로 쓰면 마지막에 있는 것은 추상적인 자료형입니다.C++에서 사용되는 멤버 함수 호출 구문을 에뮬레이트하여 함수 포인터를 구조에 넣은 다음 다음 다음을 수행하는 것을 본 적이 있습니다.

obj.get_integer(&obj);

리눅스 커널에서 파일 시스템 드라이버에 대한 인터페이스를 정의하는 데 사용됩니다.좋아할 수도 있고, 싫어할 수도 있는 글 스타일입니다.저는 그것을 너무 좋아하지 않습니다. 왜냐하면 저는 데이터를 위해 구조의 멤버를 계속 사용하고 있고 인기 있는 객체 지향 언어에서 멤버 함수 호출을 에뮬레이트하는 것을 사용하지 않기 때문입니다.

기본적으로 C++는 메서드의 주소를 포함하는 포인터 목록을 만듭니다.이 목록을 클래스 정의라고 합니다(클래스 정의에 데이터가 더 있지만 지금은 무시합니다).

순수 C에서 "클래스"를 가지는 일반적인 패턴은 "구조 클래스"를 정의하는 것입니다.구조의 필드 중 하나는 클래스의 "인스턴스"를 반환하는 팩토리 함수입니다.매크로를 사용하여 캐스트를 숨길 것을 제안합니다.

typedef struct __class * class;
typedef void (*ctor_ptr)(class);
struct class {
    char * name;
    ctor_ptr ctor;
    ... destructor and other stuff ...
}

#define NEW(clz) ((struct something *)(((struct class *)clz)->ctor(clz)))

이제 각 클래스에 대해 "구조 클래스" 유형의 구조를 만들어 클래스를 정의한 다음 해당 클래스에 저장된 생성자를 호출할 수 있습니다.파괴자 등도 마찬가지입니다.

인스턴스에 메서드를 사용하려면 클래스 구조에 메서드를 넣고 인스턴스 구조에 클래스에 대한 포인터를 유지해야 합니다.

#define NEW_SOMETHING() ((struct something *)NEW(&something_definition))
#define METHOD(inst, arg) ((struct something_class *)(((struct something *)inst)->clz)->method(inst, arg))

NEW_SOMETHING하여 "something"다에 의 새 .something_definition가 이 Method가 이 인스턴스에서 "method"를 호출합니다.다를 inst는 사실의 한 예입니다.something(클래스 포인터 같은 것을 compare합니다.)

상속을 받는 것은 조금 까다롭고 독자들을 위한 연습으로 남겨집니다.

C++에서 할 수 있는 모든 것을 순수 C로 할 수 있습니다. C++ 컴파일러는 마법처럼 CPU를 다른 것으로 대체하지 않습니다.코드가 훨씬 더 필요할 뿐입니다.

예를 보려면 glib(Gtk+ 프로젝트의 일부)를 확인하십시오.

C에서 이 작업을 수행하고 싶다고 가정하면 C++의 구조에 대한 질문이 아닙니다.

보통 하는 일은 init_whatever, 구조(또는 다른 변수)에 포인터를 가져가서 원하는 값으로 설정하는 함수를 만드는 것입니다.

전역 변수(0으로 초기화됨)인 경우 "초기화됨" 플래그를 포함하여 인수가 없는 생성자를 시뮬레이션한 다음 다른 함수에서 해당 플래그를 확인하고 플래그가 0인 경우 구조를 초기화할 수 있습니다.하지만 이것이 좋은 생각인지 전혀 확신할 수 없습니다.

다른 사람이 지적했듯이 매크로로 끔찍한 짓을 할 수도 있습니다

구조물을 생성하고 폐기하는 기능을 사용하는 것에 대해서는 이미 언급한 바 있습니다.그러나 설명에서 "기본 생성자"를 원한다고 언급했습니다. 구조 필드의 일부(모두?)를 기본값으로 초기화하려는 것입니다.

이것은 C에서 함수, 매크로 또는 혼합된 코딩 규칙을 사용하여 수행됩니다.저는 보통 다음과 같은 것을 합니다.

struct some_struct {
    int a;
    float b;
};
#define some_struct_DEFAULT { 0, 0.0f}
struct some_struct *some_struct_create(void) {
    struct some_struct *ptr = malloc(sizeof some_struct);
    if(!ptr)
        return ptr;

    *ptr = some_struct_DEFAULT;
    return ptr;
}
// (...)
struct some_struct on_stack = some_struct_DEFAULT;
struct some_struct *on_heap = some_struct_create();

malloc(),memcpy()및 C99럴:

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

#define new(TYPE, ...) memdup(&(TYPE){ __VA_ARGS__ }, sizeof(TYPE))

void * memdup(const void * obj, size_t size)
{
    void * copy = malloc(size);
    return copy ? memcpy(copy, obj, size) : NULL;
}

struct point
{
    int x;
    int y;
};

int main()
{
    int * i = new(int, 1);
    struct point * p = new(struct point, 2, 3);

    printf("%i %i %i", *i, p->x, p->y);

    return 0;
}

아래는 컨스트럭터를 이용한 간단한 프로그램입니다.명시적인 함수 호출 없이 "default_constructor" 함수가 메인 내부에서 호출됩니다.

#include        <stdio.h> 

void __attribute__ ((constructor)) default_constructor() 
{ 
    printf("%s\n", __FUNCTION__); 
} 

int main() 
{ 
    printf("%s\n",__FUNCTION__);
    return 0; 
}

출력 : default_constructor main

컨스트럭터 함수 내에 몇 가지 초기화 문을 포함하고 디스트럭터 함수를 사용하여 다음과 같이 메모리를 확보할 수 있습니다.

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

struct somestruct
{
    int     empid;
    char *  name;
};

struct somestruct * str1;

void __attribute__ ((constructor)) a_constructor() 
{ 
    str1 = (struct somestruct *) malloc (sizeof(struct somestruct));
    str1 -> empid = 30228;
    str1 -> name = "Nandan";
} 

void __attribute__ ((destructor)) a_destructor()
{
    free(str1);
}

int main() 
{ 
    printf("ID = %d\nName = %s\n", str1 -> empid, str1 -> name);
    return 0;
}

이것이 당신에게 도움이 되기를 바랍니다.

사실 그렇지 않아요.내 기억이 맞다면, 수업에 가장 가까운 곳은 구조물입니다.메모리를 할당하고 필드를 채웁니다.일반적인 컨스트럭터 타입을 어떻게 만들 수 있는지 모르겠습니다.이론적으로, 당신은 이것의 일부를 수행할 매크로를 작성할 수 있습니다.하지만 그것이 정말 가치있는 일인지는 확실하지 않습니다.

당신은 C++ 컴파일러를 보는 것이 좋을 것입니다.라이브러리와 매크로를 사용하여 C에 대한 생성자 등을 볼트로 연결하려고 하는 것보다 우아하고 표준적인 방식으로 C와 유사한 구문을 가진 언어에서 기억할 수 있는 것보다 더 많은 객체 지향적인 기능을 제공합니다.

만약 그것이 당신에게 효과가 없다면 GOobject를 보세요.http://en.wikipedia.org/wiki/GObject .GTK와 Gnome에서 사용되는 C의 객체 시스템으로 "C의 객체"를 11로 크랭크합니다.

위키피디아에서:

GLib Object System, 또는 GObject는 휴대용 객체 시스템과 투명한 상호 운용성을 제공하는 자유 소프트웨어 라이브러리(LGPL에서 다루는)입니다.

시스템은 생성자, 파괴자, 단일 상속, 인터페이스, 가상 공개 및 비공개 방식 등을 지원합니다.C++에서 동일한 작업을 수행하는 것보다 훨씬 지루하고 어렵습니다.재미있게 보내!

아니요, 구조물은 데이터 더미에 불과합니다.구조물 내에서 함수를 선언할 수 없으므로 구조물에 대한 생성자를 만들 수 없습니다.

언급URL : https://stackoverflow.com/questions/537244/default-constructor-in-c