메서드 시뮬레이션
Coddy C 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 61개 중 39번째.
구조체 내부에 함수 포인터를 배치하는 방법을 살펴보았습니다. 진짜 강력함은 동일한 구조체 타입의 서로 다른 인스턴스에 서로 다른 함수를 할당할 때 나타납니다. — 이것이 바로 다양한 동작을 가진 메서드를 시뮬레이션하는 방법입니다.
사용자 정의 가능한 로직을 포함하는 동작을 나타내는 구조체를 고려해 보겠습니다:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;인스턴스를 생성할 때, 각각에 서로 다른 함수를 연결합니다:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // 출력: Hello!
farewell.execute(); // 출력: Goodbye!
return 0;
}greet와 farewell은 모두 동일한 타입(Action)이지만, 각각에 대해 execute를 호출하면 완전히 다른 결과가 생성됩니다. 이 구조체는 단순히 데이터만 보유하는 것이 아니라, 인스턴스마다 달라지는 동작을 보유합니다.
이 패턴은 다른 언어에서 객체가 작동하는 방식을 반영합니다. 각 객체는 동일한 인터페이스를 공유하더라도 메서드에 대해 자신만의 구현을 가질 수 있습니다.
챌린지
쉬움동일한 구조체 타입의 서로 다른 인스턴스가 어떤 함수가 연결되느냐에 따라 완전히 다른 동작을 보여줄 수 있음을 증명하는 Speaker 시스템을 만들어 봅시다.
코드를 정리하기 위해 두 개의 파일을 생성합니다:
speaker.h: 매개변수가 없고 반환값이 없는SpeakFunc라는 함수 포인터 타입을 정의합니다. 그런 다음name(const char*)과SpeakFunc타입의speak함수 포인터를 포함하는Speaker구조체를 정의합니다.main.c: 헤더 파일을 포함하고 세 가지 다른 말하기 함수를 구현합니다:bark—Woof!를 출력합니다.meow—Meow!를 출력합니다.chirp—Chirp!를 출력합니다.
Speaker인스턴스를 생성하고, 입력값에 따라 각각 다른 함수를 연결한 뒤,speak메서드를 호출하여 다형성(polymorphic) 동작이 실행되는 것을 확인합니다.
프로그램은 두 개의 입력을 받습니다: 첫 번째 스피커 타입 (1은 bark, 2는 meow, 3은 chirp)과 두 번째 스피커 타입 (마찬가지로 1, 2, 또는 3)입니다.
두 개의 Speaker 인스턴스를 생성하세요. 이름은 원하는 대로 지을 수 있습니다 (예: "Dog", "Cat", "Bird" 또는 단순히 "Speaker1", "Speaker2"). 입력 코드에 따라 각 인스턴스를 적절한 함수에 연결한 다음, 각 인스턴스의 speak를 순서대로 호출하세요.
첫 번째 타입이 1이고 두 번째 타입이 2일 때의 출력 예시:
Woof!
Meow!첫 번째 타입이 3이고 두 번째 타입이 3일 때의 출력 예시:
Chirp!
Chirp!여기서 핵심은 두 스피커 모두 동일한 Speaker 타입이지만, 각자의 speak 멤버에 서로 다른 함수가 할당되었기 때문에 서로 다른 출력을 생성한다는 점입니다. 헤더 파일에 include guard를 사용하는 것을 잊지 마세요.
치트 시트
동일한 구조체 타입의 서로 다른 인스턴스는 서로 다른 함수 포인터를 가질 수 있으며, 이를 통해 다형성 동작을 구현할 수 있습니다.
함수 포인터 타입과 이를 사용하는 구조체를 정의합니다:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;서로 다른 함수가 할당된 인스턴스들을 생성합니다:
void say_hello(void) {
printf("Hello!\n");
}
void say_goodbye(void) {
printf("Goodbye!\n");
}
int main() {
Action greet = { "Greet", say_hello };
Action farewell = { "Farewell", say_goodbye };
greet.execute(); // Output: Hello!
farewell.execute(); // Output: Goodbye!
return 0;
}두 인스턴스는 동일한 타입을 공유하지만, 함수 포인터가 호출될 때 서로 다른 동작을 나타냅니다.
직접 해보기
#include <stdio.h>
#include "speaker.h"
// TODO: "Woof!"를 출력하는 bark 함수를 구현하세요.
// TODO: "Meow!"를 출력하는 meow 함수를 구현하세요.
// TODO: "Chirp!"를 출력하는 chirp 함수를 구현하세요.
int main() {
int type1, type2;
scanf("%d", &type1);
scanf("%d", &type2);
// TODO: 두 개의 Speaker 인스턴스를 생성하세요.
// TODO: type1에 따라 첫 번째 스피커의 speak 함수를 연결하세요.
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: type2에 따라 두 번째 스피커의 speak 함수를 연결하세요.
// (1 = bark, 2 = meow, 3 = chirp)
// TODO: 각 스피커의 speak를 순서대로 호출하세요.
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.