C++에서 다형성은 멤버 함수가 이를 호출/호출하는 객체에 따라 다르게 동작하게 만듭니다. 다형성은 '여러 형태를 가지다'라는 뜻의 그리스어에서 유래했습니다. 상속을 통해 관련된 클래스 계층이 있을 때 발생합니다.
예를 들어, makeSound() 함수가 있다고 가정해 봅시다. 고양이가 이 함수를 호출하면 야옹 소리를 냅니다. 소가 같은 함수를 호출하면 음메 소리를 냅니다.
하나의 함수지만 다른 상황에서 다르게 동작합니다. 함수가 여러 형태를 가지므로 다형성을 달성했습니다.
이 C++ 튜토리얼에서 배울 내용:
다형성이란 무엇인가
다형성 타입들
컴파일 타임 다형성
함수 오버로딩
연산자 오버로딩
런타임 다형성
함수 오버라이딩
C++ 가상 함수
컴파일타임 다형성 vs 런타임 다형성
다형성 타입들
C++은 두 가지 다형성 타입을 지원합니다:
컴파일타임 다형성, 그리고
런타임 다형성.
컴파일 타임 다형성
인자의 개수와 타입을 매칭하여 오버로드된 함수들을 호출합니다. 이 정보는 컴파일 타임에 존재합니다. 즉, C++ 컴파일러가 컴파일 타임에 올바른 함수를 선택합니다.
컴파일타임 다형성은 함수 오버로딩과 연산자 오버로딩을 통해 달성됩니다.
함수 오버로딩
함수 오버로딩은 비슷하지만 다른 인자들을 가진 여러 함수들이 있을 때 발생합니다. 인자들은 개수나 타입 면에서 다를 수 있습니다.
예제 1:
#include <iostream>
using namespace std;
void test(int i) {
cout << " The int is " << i << endl;
}
void test(double f) {
cout << " The float is " << f << endl;
}
void test(char const *ch) {
cout << " The char* is " << ch << endl;
}
int main() {
test(5);
test(5.5);
test("five");
return 0;
}
출력:
코드 스크린샷:
코드 설명:
코드에 iostream 헤더 파일 포함. 함수들 사용 가능해짐
코드에 std 네임스페이스 포함. 클래스 호출 없이 사용 가능
정수 매개변수 i를 받는 test 함수 생성. { 는 함수 test 본문 시작 표시
위 test 함수가 호출되면 실행될 문장
위 test 함수 본문 끝
float 매개변수 f를 받는 test 함수 생성. { 는 함수 test 본문 시작 표시
위 test 함수가 호출되면 실행될 문장
위 test 함수 본문 끝
문자 매개변수 ch를 받는 test 함수 생성. { 는 함수 test 본문 시작 표시
위 test 함수가 호출되면 실행될 문장
위 test 함수 본문 끝
main() 함수 호출. { 는 함수 본문 시작 표시
함수 test 호출하며 5를 인자 값으로 전달. 첫 번째 test 함수(정수 인자 받는 것) 호출됨
함수 test 호출하며 5.5를 인자 값으로 전달. 두 번째 test 함수(float 인자 받는 것) 호출됨
함수 test 호출하며 five를 인자 값으로 전달. 세 번째 test 함수(문자 인자 받는 것) 호출됨
프로그램 성공 실행 시 값 반환
main() 함수 본문 끝
같은 이름을 가진 세 함수가 있지만 인자 타입이 다릅니다. 다형성을 달성했습니다.
연산자 오버로딩
연산자 오버로딩에서 우리는 C++ 연산자에 새 의미를 정의합니다. 연산자 작동 방식도 변경됩니다. 예를 들어 + 연산자를 두 문자열 연결로 정의할 수 있습니다. 우리는 이를 숫자 값 더하기용 덧셈 연산자로 알고 있습니다. 우리 정의 후, 정수 사이에 놓이면 더하고, 문자열 사이에 놓이면 연결합니다.
예제 2:
#include<iostream>
using namespace std;
class ComplexNum {
private:
int real, over;
public:
ComplexNum(int rl = 0, int ov = 0) {
real = rl;
over = ov;
}
ComplexNum operator + (ComplexNum const &obj) {
ComplexNum result;
result.real = real + obj.real;
result.over = over + obj.over;
return result;
}
void print() {
cout << real << " + i" << over << endl;
}
};
int main() {
ComplexNum c1(10, 2), c2(3, 7);
ComplexNum c3 = c1+c2;
c3.print();
}
출력:
코드 스크린샷:
코드 설명:
프로그램에 iostream 헤더 포함하여 함수 사용
프로그램에 std 네임스페이스 포함하여 클래스 호출 없이 사용
ComplexNum이라는 클래스 생성. { 는 클래스 본문 시작 표시
private 접근 지정자로 변수들을 private 표시. 클래스 내부에서만 접근 가능
정수 변수 real, over 정의
public 접근 지정자로 생성자 public 표시. 클래스 외부에서도 접근 가능
클래스 생성자 생성 및 변수 초기화
변수 real 값 초기화
변수 over 값 초기화
생성자 본문 끝
+ 연산자 의미 오버라이드 필요
ComplexNum 타입의 result 데이터 타입 생성
복소수에 + 연산자 사용. 이 줄은 한 숫자의 실부를 다른 숫자의 실부에 더함
복소수에 + 연산자 사용. 이 줄은 한 숫자의 허부를 다른 숫자의 허부에 더함
프로그램 성공 실행 시 result 변수 값 반환
+ 연산자 새 의미 정의 끝, 즉 오버로딩
print() 메서드 호출
덧셈 후 새 복소수 콘솔 출력
print() 함수 본문 끝
ComplexNum 클래스 본문 끝
main() 함수 호출
실부와 허부 값들을 전달하여 더함. c1 첫 부분이 c2 첫 부분에 더해짐(10+3). c1 두 번째 부분이 c2 두 번째 부분에 더해짐(2+7)
오버로드된 + 연산자 사용한 연산 수행하고 결과를 변수 c3에 저장
변수 c3 값 콘솔 출력
main() 함수 본문 끝
런타임 다형성
이것은 객체의 메서드가 컴파일 타임이 아닌 런타임에 호출/실행될 때 발생합니다. 런타임 다형성은 함수 오버라이딩을 통해 달성됩니다. 호출될 함수는 런타임에 확정됩니다.
함수 오버라이딩
함수 오버라이딩은 기본 클래스의 함수가 파생 클래스에서 새 정의를 받을 때 발생합니다. 그때 기본 함수가 오버라이드되었다고 말합니다.
예를 들어:
#include <iostream>
using namespace std;
class Mammal {
public:
void eat() {
cout << "Mammals eat...";
}
};
class Cow: public Mammal {
public:
void eat() {
cout << "Cows eat grass...";
}
};
int main(void) {
Cow c = Cow();
c.eat();
return 0;
}
출력:
코드 스크린샷:
코드 설명:
프로그램에 iostream 헤더 파일 가져와 함수 사용
프로그램에 std 네임스페이스 포함하여 클래스 호출 없이 사용
Mammal이라는 클래스 생성. { 는 클래스 본문 시작 표시
public 접근 지정자로 곧 만들 함수를 공개 접근 가능하게 설정. 이 클래스 외부에서도 접근 가능
eat이라는 public 함수 생성. { 는 함수 본문 시작 표시
eat() 함수 호출 시 cout 함수에 추가될 문장 출력
eat() 함수 본문 끝
Mammal 클래스 본문 끝
Mammal 클래스를 상속하는 Cow 클래스 생성. Cow는 파생 클래스, Mammal은 기본 클래스. { 는 이 클래스 시작 표시
public 접근 지정자로 곧 만들 함수를 공개 접근 가능하게 설정. 이 클래스 외부에서도 접근 가능
기본 클래스에 정의된 eat() 함수 오버라이드. { 는 함수 본문 시작 표시
이 함수 호출 시 콘솔에 출력할 문장
eat() 함수 본문 끝
Cow 클래스 본문 끝
main() 함수 호출. { 는 이 함수 본문 시작 표시
Cow 클래스 인스턴스 생성하고 이름 c 부여
Cow 클래스에 정의된 eat() 함수 호출
프로그램 성공 완료 시 값 반환
main() 함수 끝
C++ 가상 함수
가상 함수는 C++에서 런타임 다형성을 구현하는 또 다른 방법입니다. 기본 클래스에서 정의되고 파생 클래스에서 재정의되는 특수 함수입니다. 가상 함수를 선언하려면 virtual 키워드를 사용해야 합니다. 키워드는 기본 클래스에서 함수 선언 앞에 와야 합니다.
가상 함수 클래스가 상속되면, 파생 클래스는 자신의 필요에 맞게 가상 함수를 재정의합니다. 예:
#include <iostream>
using namespace std;
class ClassA {
public:
virtual void show() {
cout << "The show() function in base class invoked..." << endl;
}
};
class ClassB :public ClassA {
public:
void show() {
cout << "The show() function in derived class invoked...";
}
};
int main() {
ClassA* a;
ClassB b;
a = &b;
a->show();
}
출력:
코드 스크린샷:
코드 설명:
코드에 iostream 헤더 파일 포함하여 함수 사용
코드에 std 네임스페이스 포함하여 클래스 호출 없이 사용
ClassA라는 클래스 생성
public 접근 지정자로 클래스 멤버를 공개 접근 가능하게 표시
show()라는 가상 함수 생성. public 함수
show() 호출 시 출력할 텍스트. endl은 줄바꿈을 의미하는 C++ 키워드
가상 함수 show() 본문 끝
ClassA 클래스 본문 끝
ClassA 클래스를 상속하는 ClassB라는 새 클래스 생성. ClassA는 기본 클래스, ClassB는 파생 클래스