logo

English

이곳의 프로그래밍관련 정보와 소스는 마음대로 활용하셔도 좋습니다. 다만 쓰시기 전에 통보 정도는 해주시는 것이 예의 일것 같습니다. 질문이나 오류 수정은 siseong@gmail.com 으로 주세요. 감사합니다.

C를 이용한 객체지향 프로그래밍

by digipine posted Nov 01, 2017
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print

객체지향 프로그래밍은 크게 Data Abstraction + Inheritance로 잡습니다. 각각을 C로는 어떻게 구현할 수 있는가를 보지요.

Data Abtraction : 크게 Encapsulation과 Information Hiding을 큰 맥으로 삼습니다.
(1) Encapsulation : 데이터와 처리과정을 하나의 단위로 묶는 방법입니다. 관려된 데이터를 하나의 단위로 묶는 것은 C에도 이미 struct로 처리할 수 있습니다만, C에서 처리과정, 즉 함수는 struct의 멤버로 선언될 수 없다는 맹점이 있습니다.
이러한 제약은 함수포인터를 구조체의 멤버로 선언함으로써 우회할 수 있습니다. 즉,
typedef struct _pseudo_class
{
int data_member;
int (*member_function)( struct _psedo_class* this);
} pseudo_class;

그리고 멤버함수로 쓰일 함수를 선언해 줘야 합니다. 이때 선언하는 함수가 객체의 멤버함수처럼 동작하기 위해서는 반드시 함수를 호출하는 구조체 변수의 주소를 인자로 받아야 합니다. (C++의 this포인터죠)
int pseudo_class_member_function( pseudo_class* this )
{
return 0;
}

이때, C의 구조체에는 생성자가 없으므로 각각의 멤버를 초기화해주는 별도의 함수를 작성해 줘야 합니다.
void pseudo_class_constructor( pseudo_class* this )
{
this->member_function = pseudo_class_member_function;
}

pseudo_class object; // (a)
pseudo_class_constructor( &object ); // (b)

some_value = object.member_function( &object );

이와 같은 방법을 통해서 의사-캡슐화를 수행할 수 있습니다.
이때, (a), (b)를 하나로 묶는 매크로를 선언한다면 편리하게 쓸 수 있을 겁니다.

(2) Information Hiding : 객체 외부에 대해 객체 자신의 내부 구현을 숨기는 것을 말하죠. 하지만 불행히도 C에는 C++과 같이 접근을 통제하는 키워드가 존재하지 않습니다. 따라서 이 역시 편법을 써야 합니다. 디자인 패턴을 공부하셨다면 Representation 패턴에 대해 알고 계실 겁니다. 즉, 클래스의 내부 구성을 별도의 클래스로 분리하여 캡슐화 하는 기법이죠. 표상 패턴을 사용하면 다음과 같은 형태로 헤더파일이 구성 됩니다.

struct _inner_rep;

typedef struct _pseudo_class
{
struct _inner_rep* rep;
int (*member_function)( struct _psedo_class* this);
} pseudo_class;

void pseudo_class_constructor( pseudo_class* this );
int pseudo_class_member_function( pseudo_class* this );

그리고 이들을 구현한 C소스 파일에 _inner_rep와 컨트럭터, member_function의 구현을 넣으면 헤더파일만 include한 외부 소스에서는 이들의 내부 구현이 가려지게 되는 효과가 옵니다. 이때, 각각의 함수를 static으로 선언한다면 좀 더 확실히 은익하는 효과가 있겠지요.
전역 데이터의 경우도 C소스파일에서 static으로 선언하면 외부 모듈에 대해 가져지게 되는 효과가 있으므로 이를 적절히 활용하면 information hiding에 유효하게 사용할 수 있습니다.


Inheritance : 사실상 제일 까다로운 부분입니다. Data Abstraction은 struct의 범위 내에서 어느정도 커버할 수 있지만, 상속성을 순수 C문법으로 구현하기 위해서는 좀더 많은 편법이 필요하죠.
(1) 멤버 상속 : 한가지 방법은 union을 이용하는 겁니다.
struct base
{
int i;
int (*base_function)( struct base* this );
};

union derived
{
struct base b;
struct
{
int i; // 베이스 클래스의 멤버를 그대로 써줍니다.
int (*base_function)( struct base* this );

int j; // 자식 클래스의 멤버를 새로 선언합니다.
int (*derived_function)( struct derived* this );
};
};
물론 위의 코드는 비표준의 익명구조체 멤버를 쓰고 있으므로 모범답안이라고 볼 수는 없습니다만, 베이스클래스의 멤버그대로를 마치 자신의 멤버인것처럼 쓸 수 있도록 해주는 장점이 있습니다. 하지만 많은 수의 컴파일러에서 지원하고 있지 않으므로 널리 쓸 수는 없을 겁니다.
익명 구조체를 지원하지 않는 경우라면 어쩔 수 없이 구조체안에 베이스클래스를 나타내는 구조체를 멤버로 포함하여 설계하는 수밖에는 없습니다. 이럴 경우에는 베이스클래스의 멤버를 참조할때 두번의 인디렉션이 필요하게 되는 불편함이 있겠죠.

(2) 멤버함수 오버라이딩 : 이 부분은 간단합니다. 각각의 클래스의 constructor에서 멤버함수 포인터를 초기화할때 서로 다른 함수를 포인팅하도록 세팅해주면 됩니다. 이렇게 하면 가상함수와 동적 바인딩도 자연스럽게 해결되지요.
좀 더 신경써서 구현한다면 함수포인터 테이블을 각각의 클래스마다 별도로 생성하여 일괄적으로 assign해 주는 방법이 있겠습니다. (이 방법은 실제로 C++컴파일러가 동적바인딩을 구현하기 위해 사용하는 방법이 되겠습니다)

실제로, C++초창기에는 C++컴파일러가 구현되지 않아서 C++소스를 C소스로 컨버팅하여 C컴파일러로 컴파일하여 오브젝트 코드를 생성하는 방법을 쓴 시기가 있었습니다. 조금만 머리를 써 보시면 C++소스를 그와 동등한 C코드로 변환하는 방법이 얼마든지 존재한다는 걸 알 수 있을 겁니다.

TAG •
?

List of Articles
No. Subject Author Date Views
20 C++ 컴파일 오류(error): variable 'std::istringstream sstream' has initializer but incomplete type digipine 2017.11.02 9
19 fopen 파일 열기 모드 옵션 정리 digipine 2017.11.02 8
18 make -j 옵션으로 컴파일 속도 최적화 하기 digipine 2017.11.01 2
17 [Linux] Pthread 사용법, Thread 동기화 총정리 digipine 2017.11.01 3
16 [linux] zlib build 방법 digipine 2017.11.01 2
15 Linux C 언어로 Shell 명령어 실행하기 digipine 2017.11.01 3
14 Introduce to Singly-linked List file digipine 2017.11.01 2
13 [shared lib] so 동적 라이브러리 만들기와 사용법 - 리눅스 digipine 2017.11.01 2
12 Unix C/C++ Input and Output Function Reference digipine 2017.11.01 2
11 wchar_t에 대하여 digipine 2017.11.01 3
10 C 에서 Overloading 구현 digipine 2017.11.01 4
» C를 이용한 객체지향 프로그래밍 digipine 2017.11.01 2
8 Callback in C++ 와 Delegate 차이점 digipine 2017.11.01 2
7 Solaris에서 pmap을 이용하여 백그라운드 프로세스 메모리 크기 구하기 digipine 2017.10.29 2
6 Solaris 10에 개발 Tool (gcc,vim,gdb) 설치 digipine 2017.10.29 2
5 brute-force 알고리즘을 이용한 패턴 위치 찾기 digipine 2017.10.29 3
4 MD5 파일 변조 검사 관련 소스 (리눅스/Windows) digipine 2017.10.29 2
3 STL MAP 예제로 공부하기 digipine 2017.10.29 5
2 C++에서 extern의 역할, 기능 digipine 2017.10.29 4
1 [C/C++] 현재시간 구하기 digipine 2017.10.28 3
Board Pagination Prev 1 Next
/ 1