재밌는 cpp 04를 시작해보겠습니다!
Subtype polymorphism, abstract classes, interfaces
우선 다형성 용어에 대해 확실히 이해해보자.
다형성의 종류는 크게 3가지로 나뉘어 진다.
지금까지 계속사용한 다형성이다.
간단히 말하자면 함수 다형성, 연산자 다형성이 임시다형성에 해당된다.
어떤 종류의 컨테이너나 type에 대한 제약없이 사용할 수 있는 함수를 제네릭 함수라고 부른다.
일반함수 (Generic function), 일반 프로그래밍 (Generic programming) 이 메게변수 다형성에 해당된다.
제네릭 함수는 제네릭 형식 매개 변수를 사용하여 선언된 함수입니다. 클래스 또는 구조체의 메서드이거나 독립 실행형 함수일 수 있습니다. 단일 제네릭 선언에서 제네릭 형식 매개 변수를 대체하는 실제 형식만 다른 함수 제품군을 암시적으로 선언합니다.
무슨 말인가 와닿지 않았는데, 찾아보니, 선언시 템플릿을 이용해 함수 내용을 일반화 시켜 놓고, 런타임에서 확정하여 사용하는 방법이였다.
generic <typename ItemType>
void G(int i) {}
ref struct A {
generic <typename ItemType>
void G(ItemType) {}
generic <typename ItemType>
static void H(int i) {}
};
int main() {
A myObject;
// generic function call
myObject.G<int>(10);
// generic function call with type parameters deduced
myObject.G(10);
// static generic function call
A::H<int>(10);
// global generic function call
G<int>(10);
}
인터페이스 상속이 이에해당된다.
인터페이스 상속을 이해할려면 추상클래스 개념을 알아야한다.
추상 클래스란?
순수 가상 메서드를 가지고 있는 클래스
구상클래스는 실제 개체를 생성할수 있는 클래스
class Musician //추상 클래스 (순수 가상 메서드를 갖고 있다.)
{
string name;
public:
virtual void Play() = 0; //순수 가상 메서드 (추상 메서드)
}
int main()
{
Musician *A = new Musician();
}
위에 코드에서 순수 메서드를 선언했는데, 정의를 하지 않아서 컴파일 에러가 난다.
가상 메서드 - 함수 본문을 정의할수 있다., (파생형식에서 재정의 하면 실제 개체의 메서드를 수행한다.)
순수 가상 메서드 - 함수 본문을 정의할수 없다. (파생 형식개체는 생성할 수 있습니다., 대신 파생형식에서 재정의 하지 않으면 파생 형식 개체도 생성할 수 없습니다. - 이럴떄 추상클래스가 됨..)
추상클래스 - 순수 가상 메서드를 가지고 있는 클래스
class Musician //추상 클래스 (순수 가상 메서드를 갖고 있다.)
{
string name;
public:
virtual void Play() = 0; //순수 가상 메서드 (추상 메서드)
}
class pianist: public Musicaian
{
virtual void play (void)
{
std::cout << "succese" << std::endl;
}
}
int main()
{
Musician *A = new pianist();
}
위처럼 play함수를 planist처럼 재정의 해줘야 구상 클래스로 사용할수 있다.
이제 인터페이스로 넘어가자.
#define interface struct
interface Iplay{
virtual void Play() = 0;
};
class Man : public Iplay{
virtual void Play(){
std::cout << " 재정의 " << std::endl;
}
}
이런식으로 인터페이스랑 추상클래스랑 다른건, 클러스나 struct안에, 가상순수함수만 있으면 인터페이스라고 칭한다. (위에 추상클래스에서는 name이 있어서 추상클래스라 했음.)
진짜 벙쪘던 부분이있다.
class 사람 {
};
class 기타 : public 사람 {
};
class 드럼 : public 사람 {
};
class 연주 {
사람 *A[2];
A[0] = new 기타;
A[1] = new 드럼;
};
int main
{
사람 A = new드럼; //이게 왜 되게 해놨을까 많이 의아했다.
}
연주를 보면, 기타와 드럼을 하나의 배열에 넣을수 있다. 이렇게 부모객체에 자식 클래스를 넣을수 있게 되면 위와같이 배열로 사용할수 있다는 장점이 생긴다!
위와같은걸 형식 다형성이라고 한다.
클래스 안에, 순수함수가 하나 포함되고 이외에 순수함수가 아닌것도 포함된 경우를 추상 클래스 라고 한다.
상속은 알지? ㅎㅎ
자 이제 과제를 해보자.!
케스팅을 왜할까??
const Animalj = new Dog();
이렇게 업캐스팅 하도록 만들어 놓은 이유가
하나의 Animalj로 Dog도 받을수 있고 Cat도 받을 수 있고 자식인스턴스들은 다 받을수 있어서
코드 재활용에 유용하다고 하네요.
뭔 말이냐면
22 Animal *zz = test("Dog");
23 std::cout << zz->getType() << std::endl;
24 zz = test("Cat");
25 std::cout << zz->getType() << std::endl;
26 std::cout << std::endl;
하나의 test함수로 Dog의 return값도 받을수 있고, Cat의 return 값도 받을수 있다!!!!!!
04, 01
Animal *test(string want)
{
if(want == "Dog")
return new Dog();
else
return new Cat();
}
main(void)
{
Animal *i = test("Dog");
i->makeSound();
i = test("Cat");
i->makeSound();
}
위에 코드 원본
부모 클래스 Animal
자식 클래스 Dog, Cat
9 const Animal* meta = new Animal();
10 std::cout << std::endl;
11 const Animal* j = new Dog();
12 std:: cout <<std::endl;
13 const Animal* i = new Cat();
14 std::cout << std::endl;
부모클래스 자료형에 자식클래스 넣는거는 된다.!
15 const Dog* test = new Animal();
반대로 자식클래스 자료형에 부모클래스를 넣어보았다.
당연히 에러가 났다.
15 const Dog* test = (Dog *)new Animal();
17 test->makeSound();
자료형 타입이 안맞다니까 형변환 시키고 넣었다.
메게 함수까지 실행 시켜 보았는데, makeSound는 Animal에 virtual로 선언되어있고, 자식에는 그냥 makeSound로 선언되어있다. 형태는 똑같다.
test->makeSound(); 출력결과, animal_makeSound가 나왔다.
잘 이해가 안된다. 업케스팅 다운케스팅 공부를 해봐야할거 같다.
11 const Animal *A[50];
12
13 for (int index = 0; index < 50; index++)
14 {
15 if(index % 2 == 0)
16 A[index] = new Dog();
17 else if(index % 2 == 1)
18 A[index] = new Cat();
19 std::cout <<std::endl;
20 }
21 std::cout << std::endl;
22
클래스도 이중 포인터로 선언해서 쓸수 있다.
const Animal *A[50];
반복문 돌리면서 채워주면 된다. 생성자들은 순서대로 생성된다. (Brain은 Cat 클래스안에 있는 클래스다.)
소멸자는 생성순과 반대다. 아래 사진처럼 출력된다.