2016-09-13 2 views
0

이 게시물에 기반하여 C에서 데이터 캡슐화를 시도했습니다. https://alastairs-place.net/blog/2013/06/03/encapsulation-in-c/. 헤더 파일에 C에서 데이터 캡슐화를 사용하는 OOP 프로그래밍

제가 가지고 'functions.c'에서

#ifndef FUNCTIONS_H 
#define FUNCTIONS_H 

// Pre-declaration of struct. Contains data that is hidden 
typedef struct person *Person; 


void getName(Person obj); 
void getBirthYear(Person obj); 
void getAge(Person obj); 
void printFields(const Person obj); 

#endif 

은 내가 아니라 함수를 정의하는

#include "Functions.h" 

enum { SIZE = 60 }; 

struct person 
{ 
    char name[SIZE]; 
    int birthYear; 
    int age; 
}; 

PLUSS 같은 구조를 정의 하였다. main.c의에서

나는이 :

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

int main(void) 
{ 
    // Works because *Person makes new a pointer 
    Person new = malloc(sizeof new); 

    getName(new); 
    getAge(new); 
    getBirthYear(new); 
    printFields(new); 

    free(new); 

    return 0; 
} 

내가 Person new를 사용하는 경우, new 이미 때문에 typedef struct person *Person;의 포인터 것을, 그것이 사실입니다.

이 가능하며, 내 struct person

에서 선언 한 몸과 멤버를 볼 수 링커이 가능한 경우에만 사용하여 포인터가 어떻게

?

지금처럼 functions.h의 다른 struct를 만들기 위해 내 경우에는 OOP의 prinicples을 구현하는 올바른 (유일한) 방법입니다 모두의

typedef struct classPerson 
{ // This data should be hidden 
    Person data; 

    void (*fPtrGetName)(Person obj); 
    void (*fPtrBirthYear)(Person obj); 
    void (*fPtrGetAge)(Person obj); 
    void (*fPtrPrintFields)(const Person obj); 
} ClassPerson; 
+0

절대'typedef' 포인터가 없습니다! 그것은 혼동을 보장하고 객체에'const' 한정자를 사용하는 것을 금지합니다. 그것이 링크 된 사이트에서 온 것이라면 틀립니다. 그냥'typedef'구조체'. 그리고 그것은 OOP가 아니라 정보 숨기기입니다. – Olaf

+0

@Olaf 정보 숨기기, 일명 개인 캡슐화는 OOP의 필수 구석 중 하나입니다. 나머지는 모듈 식 디자인과 상속입니다. 이 3 가지 방법 모두를 통해 달성 할 수 있습니다. 적절한 OOP를 위해서는 그 외에는 다른 것을 필요로하지 않습니다. OO를 지원하는 언어로 우연히 발견 할 수있는 다른 모든 것들은 다양한 유용성을 가지고 있습니다. – Lundin

+0

@ 런 딘 :별로.OOP의 주된 문제는 그것들을 ** 조작하는 기능을 제공하는 객체입니다 **. 그래도 개체와 메서드가 분리되어 있으면 OOP가 없습니다. 모듈 식 프로그래밍만큼이나 OOP가 아닙니다. 이것은 모든 현대 프로그래밍 언어의 표준입니다. 나는 OOP의 필수 부분 인 상속에 동의하는지 잘 모르겠습니다. 오리 타이핑은? OOP가 없으며 인터페이스가 동일합니다. – Olaf

답변

1

첫째, 뒤에 포인터를 숨기지하는 것이 낫다 typedef를 사용하지만 호출자가 포인터 유형을 사용할 수있게합니다. 이렇게하면 코드를 읽고 유지할 때 모든 종류의 오해를 예방할 수 있습니다. 예를 들어, void printFields(const Person obj);Person이 포인터 유형이라는 것을 깨닫지 못한다면 난센스처럼 보입니다.

Have I understood correctly, that when I use Person new, new is already pointer because of typedef struct person *Person;.

예. 당신은 언급 된 typedef 때문에 혼란스러워합니다.

How is it possible, that linker cannot see the body and members that I have declared in my ´struct person´?

링커는 링크 된 모든 것을 볼 수 있거나 실제로 실행 파일로 끝나지 않을 수 있습니다.

컴파일러 그러나 "번역 단위"(대략적으로 .c 파일과 포함 된 모든 헤더를 의미)에서 작동합니다. 호출자의 번역 단위를 컴파일 할 때 컴파일러는 functions.c를 보지 않으며 functions.h 만 보게됩니다. 그리고 functions.h에서 구조체 선언은 불완전 유형을 제공합니다. 의미는 "이 구조체 정의는 다른 곳에 있습니다". 가끔 불투명 포인터 또는 불투명 타입이라고 C.에이 개념을 적절한 객체 지향 프로그래밍을 수행하려는 경우

Is this only possible using pointer?

예, 그것은 유일한 방법입니다. (당신은 또한 static 키워드하지만 "가난한 사람의 개인 캡슐"을 달성 할 수 있지만.이 스레드 안전하지 않을 것이기 때문에 일반적으로 정말 권장하지 않습니다.)

거의

Is the correct (and only) way to implement OOP prinicples in my case to make a different struct in functions.h like so:

, 그래 (위에서 언급 한 포인터 typedef에 대한 nit-pick을 제외하고). 공용 함수에 대한 함수 포인터를 사용하는 것은 필요하지 않지만 다형성을 구현하는 방법입니다.

예제에는 "생성자"와 "소멸자"가 없습니다. 그들 없이는 코드가 의미가 없을 것입니다. malloc 및 무료 호출은 호출자가 수행하지 않은 호출 내에서 수행되어야합니다.

+1

글쎄,'const person obj'는 포인터 타입에서'const'를 정규화하기를 원하지 않습니다. – Olaf

+0

@Olaf 멋지게 목격됩니다. 실제로 포인터는 포인터 자체를 읽기 전용으로 만들고 의도 한 데이터는 의도하지 않았습니다. 그래서 그들은 typedef 뒤에 포인터를 숨기고 나서 몇 줄을 잡았고 발에 총을 발사하고 미묘한 버그를 썼습니다. – Lundin

+0

사실, 헝가리 표기법처럼 뭔가를하지 않는 한 포인터 - 비하인드 타입 선언으로 const의 정확성을 얻을 수 없다는 것을 의미합니다. typedef const struct person * cPerson; // 아주 나쁜 생각' – Lundin

0

typedef가 있든 없든 C에서는 불완전한 유형을 선언하여 데이터를 숨 깁니다.

extern size_t fread (void *__restrict __ptr, size_t __size, 
        size_t __n, FILE *__restrict __stream) __wur; 

FILE이 같은 것을 선언 :

struct _IO_FILE; 
typedef struct _IO_FILE FILE; 

당신이 유형 FILE의 변수를 정의 할 수 없습니다 stdio.h을 사용하여 /usr/include/stdio.h, 당신은 이 (3) FILE * 인자를 FREAD 찾을 수 있습니다 유형 FILE이 불완전하기 때문에 선언되었지만 정의되지 않았습니다. 그러나 모든 데이터 포인터가 같은 크기이기 때문에 행복하게 FILE *을 전달할 수 있습니다. 을 열어 (3)을 열어야 파일을 열어 볼 수 있습니다. 으로

부분적으로 귀하의 경우와 같이 유형을 정의 :

struct classPerson 
{ // This data should be hidden 
    Person data; 

    void (*fPtrGetName)(Person obj); 
... 
}; 

조금 까다 롭습니다. 우선, 당신은 정말 좋은 이유, 즉 fPtrGetName의 두 가지 구현이 구현되어야합니다. 그렇지 않으면 당신은 단지 OOP의 제단에 복잡성을 구축하고 있습니다.

좋은 이유의 좋은 예는 입니다. (2)입니다. 유닉스 도메인 소켓이나 네트워크 소켓을 바인딩 할 수있다. 두 유형 모두 struct sockaddr으로 표시되지만 이는 단지 struct sockaddr_unstruct sockaddr_in의 스탠드 인 유형입니다. struct sockaddr을 사용하는 함수는 이러한 모든 구조가 sun_family 멤버로 시작하고 이에 따라 분기한다는 사실에 달려 있습니다. 등등, 다형성 : 하나의 기능, 많은 유형.

함수 포인터로 가득 찬 구조체의 예를 보려면 SQLite을 권장합니다. API에는 구조체가로드되어 OS와 분리되어 사용자가 플러그인을 정의 할 수있게합니다.

나는 그렇게 말할 수 있다면, fPtrGetName은 끔찍한 이름입니다. 함수 포인터라는 것이 흥미롭지는 않습니다. (논쟁!) "get"은 인수를 취하지 않는 함수의 노이즈입니다. 비교

struct classPerson sargent; 
sargent.fPtrGetName(); 
sargent.name(); 

오히려 사용 하시겠습니까? I/O 기능을 위해 "get"(또는 유사)을 예약합니다. 적어도 한 주머니에서 다른 주머니로 옮기는 것이 아니라 뭔가를 얻는 것입니다. 설정을 위해 C++에서 함수를 오버로드하여 get/set 함수의 이름이 같지만 C에서는 예를 들어. set_name(const char name[]).