객체지향에서 다형성은 여러가지 형태를 가질 수 있는 능력을 의미한다. C++에서 다형성은 상속, 오버로딩, 오버라이딩, 템플릿에 의한 다형성 등으로 구현된다.
다형성을 이용하면 코드가 간결해지고, 가독성이 향상되고 코드의 유연성이 증가한다.
#include <iostream>
using namespace std;
class Animal{
public:
virtual void Bark(){
cout << "Ani";
}
};
class Dog : public Animal{
public:
void Bark() override{
cout << "Dog";
}
};
class Cat : public Animal{
public:
void Bark() override{
cout << "Cat";
}
};
int main() {
Animal* dog = new Dog();
Animal* cat = new Cat();
dog->Bark(); // 출력: Dog
cat->Bark(); // 출력: Cat
return 0;
}
이는 가상함수 vitrual과 상속을 통해서 다형성을 구현한 것이다. Animal이라는 부모 클래스로부터 Dog와 Cat가 파생되었다.
이들은 Bark() 라는 가상함수를 재정의하여 자신만의 울음소리를 출력한다. 이렇게하면 Animal클래스의 포인터를 통해 Dog와 Cat클래스의 Bark() 를 호출할 수 있다.
아마도 가장 많이 접할 수 있는 형태의 다형성일 것이다.
같은 이름의 함수를 여러 개 정의하는 것을 의미한다. 각각의 함수들은 매개변수의 타입이나 갯수가 다르게 설정된다. 이때, 매서드의 이름이 같아야하고, 파라미터의 갯수는 달라야 한다.
부모 클래스의 매서드를 자식 클래스에서 재정의하는 것이다. 이때, 부모 클래스에 오버라이드하고자 하는 매서드가 있어야하며, 매서드의 이름이 같아야하고, 리턴형까지 같아야한다.
일반화 프로그래밍을 가능하게 만들어주는 것(C#의 제네릭). 이를 사용하면 여러 타입에 대응되는 하나의 매서드나 객체를 만들 수 있다.
int add(int x, int y) {
return x + y;
}
double add(double x, double y) {
return x + y;
}
위의 예시는 두 매개변수를 더한 값을 리턴해주는 매서드이다. 바로 위에서 언급한 오버로딩의 한 예시이다. 하지만, 각각의 자료형에 따라서 새로운 매서드가 필요하다는 단점이 있다.
template <typename T>
T add(T x, T y){
return x + y;
}
int intAdd = add<int>(1, 3);
float floatAdd = add<float>(1.1, 1.3);
위와 같이 템플릿을 사용한다면, 코드 하나로 모든 자료형에 대해서 유연하게 적용할 수 있다.
이는 template 이라는 예약어로 정의한다.typename을 T라고 부르고, 아래에 위치한 함수를 템플릿으로 정의하겠다는 의미이다. 이때, T는 일반적으로 사용되는 문자이며, 다른 문자를 사용해도 무방하다.
💡 템플릿 타입은 컴파일러가 컴파일 시에 사용되고자 하는 타입으로 클래스 코드를 생성해 낸다.
만약 위의 예시처럼 int 와 float 형식으로 사용한다면 컴파일 시에 컴파일러가 int, float 타입의 클래스를 만들어낸다.