C에서 컴파일 타임 캡슐화 란 무엇입니까?


9

C ++에 비해 C의 장점을 연구 할 때이 단락을 보았습니다.

C에서 캡슐화를 수행하는 표준 방법은 구조체를 전달 선언하고 함수를 통해 데이터에 대한 액세스 만 허용하는 것입니다. 이 방법은 또한 컴파일 시간 캡슐화를 만듭니다. 컴파일 시간 캡슐화를 통해 클라이언트 코드 (우리의 인터페이스를 사용하는 다른 코드)를 다시 컴파일하지 않고도 데이터 구조 멤버를 변경할 수 있습니다. 반면에 클래스를 사용하여 캡슐화 C ++을 수행하는 표준 방법에는 개인 멤버 변수를 추가하거나 제거 할 때 클라이언트 코드를 다시 컴파일해야합니다.

구조체를 선언하고 함수를 통해 멤버에 액세스하는 것이 구조체의 구현 세부 정보를 숨기는 방법을 이해합니다. 내가 이해하지 못하는 것은 구체적으로 다음과 같습니다.

컴파일 시간 캡슐화를 통해 클라이언트 코드 (우리의 인터페이스를 사용하는 다른 코드)를 다시 컴파일하지 않고도 데이터 구조 멤버를 변경할 수 있습니다 .

어떤 시나리오에서 적용 가능합니까?


기본적으로 struct내부는 알 수없는 블랙 박스입니다. 클라이언트가 내부를 모르면 직접 액세스 할 수 없으며 마음대로 변경할 수 있습니다. 이것은 OOP의 캡슐화와 유사합니다. 내부는 비공개이며 공용 메소드를 사용하여 오브젝트를 변경하기 만합니다.
Sulthan

항상 그런 것은 아닙니다. 구조체의 멤버를 추가 / 제거하기로 결정하면 크기가 변경됩니다. 클라이언트 코드를 다시 컴파일해야합니다.
DarkAtom

2
@DarkAtom 사실이 아닙니다! 클라이언트가 내용 ( 불투명 한 구조)을 모르는 경우 크기를 모르므로 크기를 변경해도 문제가되지 않습니다.
Adrian Mole

1
@DarkAtom : 함수를 통해서만 구조에 액세스 할 수있게하려면 함수를 통한 할당 만 포함됩니다. 라이브러리는 구조를 할당하는 기능을 제공하며 클라이언트는 크기를 알 수 없습니다. 크기를 변경해도 클라이언트를 다시 컴파일 할 필요가 없습니다.
에릭 Postpischil

3
이것은 C ++에서 동일한 아이디어를 구현할 수있는 것처럼 기술적으로 "C ++에 비해 C의 장점"이 아닙니다. "pimpl"관용구를 찾으십시오 .
user4815162342

답변:


4

이러한 상황이 발생할 수있는 실제 시나리오는 하드 디스크 공간이 매우 제한된 날에 작성된 데이터베이스 라이브러리가 단일 바이트를 사용하여 날짜의 '연도'필드를 저장하는 경우입니다 (예 : 11-NOV-1973). 73그 해에 있을 것 입니다). 그러나 2000 년이되었을 때 이것은 더 이상 충분하지 않으며 연도는 짧은 (16 비트) 정수로 저장되어야했습니다. 이 라이브러리와 관련된 (훨씬 단순화 된) 헤더는 다음과 같습니다.

// dbEntry.h
typedef struct _dbEntry dbEntry;

dbEntry* CreateDBE(int day, int month, int year, int otherData);
void DeleteDBE(dbEntry* entry);
int GetYear(dbEntry* entry);

그리고 '클라이언트'프로그램은 다음과 같습니다.

#include <stdio.h>
#include "dbEntry.h"

int main()
{
    int dataBlob = 42;
    dbEntry* test = CreateDBE(17, 11, 2019, dataBlob);
    //...
    int year = GetYear(test);
    printf("Year = %d\n", year);
    //...
    DeleteDBE(test);
    return 0;
}

'원본'구현 :

#include <stdlib.h>
#include "dbEntry.h"

struct _dbEntry {
    unsigned char d;
    unsigned char m;
    unsigned char y;    // Fails at Y2K!
    int dummyData;
};

dbEntry* CreateDBE(int day, int month, int year, int otherData)
{
    dbEntry* local = malloc(sizeof(dbEntry));
    local->d = (unsigned char)(day);
    local->m = (unsigned char)(month);
    local->y = (unsigned char)(year % 100);
    local->dummyData = otherData;
    return local;
}

void DeleteDBE(dbEntry* entry)
{
    free(entry);
}

int GetYear(dbEntry* entry)
{
    return (int)(entry->y);
}

그런 다음 Y2K의 접근 방식에서이 구현 파일은 다음과 같이 변경됩니다 (다른 모든 항목은 그대로 유지됨).

struct _dbEntry {
    unsigned char d;
    unsigned char m;
    unsigned short y;   // Can now differentiate 1969 from 2069
    int dummyData;
};

dbEntry* CreateDBE(int day, int month, int year, int otherData)
{
    dbEntry* local = malloc(sizeof(dbEntry));
    local->d = (unsigned char)(day);
    local->m = (unsigned char)(month);
    local->y = (unsigned short)(year);
    local->dummyData = otherData;
    return local;
}

새로운 (Y2K 안전) 버전을 사용하도록 클라이언트를 업데이트해야 할 경우 코드를 변경할 필요가 없습니다. 실제로 다시 컴파일 필요조차 없습니다 . 업데이트 된 객체 라이브러리에 다시 연결 하면 충분할 수 있습니다 .


2

참고 : 다음 목록은 전체 목록이 아닙니다. 편집은 환영합니다!

적용 가능한 시나리오는 다음과 같습니다.

  • 어떤 이유로 재 컴파일을 원하지 않는 다중 모듈 응용 프로그램.
  • (게시 된) 구조를 변경할 때마다 라이브러리 사용자가 강제로 다시 컴파일하지 않도록하려는 라이브러리에 사용되는 구조.
  • 모듈이 작동하는 다른 플랫폼에서 다른 요소를 포함하는 구조.

이 종류의 가장 알려진 구조는 FILE입니다. fopen()성공하면 전화 를 걸어 포인터를 얻습니다. 그런 다음이 포인터는 파일에서 작동하는 서로 다른 함수로 전달됩니다. 그러나 포함 된 요소 및 크기와 같은 세부 정보는 알지 못합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.