publicclassChildClassextendsParentClass{ @Override publicvoidmethodA() { System.out.println("This is Child A Method and I am Calling Parents Method A"); super.methodA(); }
@Override publicvoidmethodB() { System.out.println("This is Child B Method and I am Calling Parents Method B"); super.methodB(); }
@Override publicvoidwhoAmI() { System.out.println("I am instance Of B"); } }
1 2
ChildClassc=newChildClass(); c.methodB();
내가 예상한 동작은 다음과 같았다.
1 2 3
This is Child B Method and I am Calling Parents Method B This is Parent B Method and I am Calling Method A This is Parent A Method
하지만 결과는 다음과 같았다.
1 2 3 4
This is Child B Method and I am Calling Parents Method B This is Parent B Method and I am Calling Method A This is Child A Method and I am Calling Parents Method A This is Parent A Method
classParent { public: voidmethodA(){ std::cout<<"This is Parent A Method"<<std::endl; }
voidmethodB(){ std::cout<<"This is Parent B Method and I am Calling Method A"<<std::endl; methodA(); }
voidwhoAmI(){ std::cout<<"I am instance Of A"<<std::endl; } };
classChild : public Parent { public: voidmethodA(){ std::cout<<"This is Child A Method and I am Calling Parents Method A"<<std::endl; Parent::methodA(); }
voidmethodB(){ std::cout<<"This is Child B Method and I am Calling Parents Method B"<<std::endl; Parent::methodB(); }
voidwhoAmI(){ std::cout<<"I am instance Of B"<<std::endl; } };
결과는 내 예상과 같았다.
1 2 3
This is Child B Method and I am Calling Parents Method B This is Parent B Method and I am Calling Method A This is Parent A Method
왜 이런일이 일어났을까? 아무래도 자바의 오버라이딩은 C++의 virtual 키워드를 포함하고 있다는 결론을 내릴 수 밖에 없었다. 즉 부모의 methodB를 호출했더라도 methodB에서 호출하는것은 methodA이고 현재 인스턴스는 Child의 인스턴스이기 때문에 Child의 methodA가 호출이 된다.
조금 더 알아보니 다음과 같은 내용을 찾을 수 있었다.
In Java, all non-static methods are by default “virtual functions.” Only methods marked with the keyword final, which cannot be overridden, along with private methods, which are not inherited, are non-virtual.
그럼 C++은 왜그럴까? 답은 간단하다. 함수가 virtual이 아니기 때문이다. virtual로 지정된 클래스는 virtual table이 존재하고 런타임에 어느 함수를 실행할지 결정을 할 수 있다. 그렇지만 위와 같은 경우는 함수가 virtual 키워드를 포함하지 않기 때문에 부모의 methodB에서 methodA를 호출하는 경우 참조할 함수는 부모의 methodA밖에 존재하지 않는다. 쉽게 말해 virtual table에서 어떤 함수를 호출해야 할지 확인할 방법도 없는 상태에서 parent의 methodB가 알고있는 methodA는 parent의 methodA밖에 없기 때문에 parent의 methodA를 호출하게 되는것이다.
그럼 이를 자바와 같이 바꾸려면 어떻게 해야할까? 이것도 간단하다. Parent를 virtual class로 만들어주면 된다.
classParent { public: virtualvoidmethodA(){ std::cout<<"This is Parent A Method"<<std::endl; }
virtualvoidmethodB(){ std::cout<<"This is Parent B Method and I am Calling Method A"<<std::endl; methodA(); }
virtualvoidwhoAmI(){ std::cout<<"I am instance Of A"<<std::endl; } };
classChild : public Parent { public: voidmethodA()override{ std::cout<<"This is Child A Method and I am Calling Parents Method A"<<std::endl; Parent::methodA(); }
voidmethodB()override{ std::cout<<"This is Child B Method and I am Calling Parents Method B"<<std::endl; Parent::methodB(); }
voidwhoAmI()override{ std::cout<<"I am instance Of B"<<std::endl; } };
1 2 3 4
This is Child B Method and I am Calling Parents Method B This is Parent B Method and I am Calling Method A This is Child A Method and I am Calling Parents Method A This is Parent A Method
그럼 자바에서 다음과 같은 출력을 만들려면 어떻게 해야할까?
1 2 3
This is Child B Method and I am Calling Parents Method B This is Parent B Method and I am Calling Method A This is Parent A Method
사실 이는 근본적으로 dynamic dispatching 때문에 불가능하다. 그렇기 때문에 이를 우회하는 여러 방법들을 사용해야한다.
publicclassParentClass { privatevoidmethodA() { System.out.println("This is Parent A Method"); }
publicvoidmethodB() { System.out.println("This is Parent B Method and I am Calling Method A"); methodA(); } }
publicclassChildClassextendsParentClass{ publicvoidmethodA() { System.out.println("This is Child A Method and I am Calling Parents Method A"); }
@Override publicvoidmethodB() { System.out.println("This is Child B Method and I am Calling Parents Method B"); super.methodB(); } }
방법 2. final로 선언한다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
publicclassParentClass { finalvoidmethodA() { System.out.println("This is Parent A Method"); }
publicvoidmethodB() { System.out.println("This is Parent B Method and I am Calling Method A"); methodA(); } }
publicclassChildClassextendsParentClass{ @Override publicvoidmethodB() { System.out.println("This is Child B Method and I am Calling Parents Method B"); super.methodB(); } }
privatevoidinterMethodA() { System.out.println("This is Parent A Method"); }
publicvoidmethodB() { System.out.println("This is Parent B Method and I am Calling Method A"); interMethodA(); } }
publicclassChildClassextendsParentClass{ @Override publicvoidmethodA() { System.out.println("This is Child A Method and I am Calling Parents Method A"); super.methodA(); }
@Override publicvoidmethodB() { System.out.println("This is Child B Method and I am Calling Parents Method B"); super.methodB(); } }
publicclassParentClass { publicvoidmethodA() { System.out.println("This is Parent A Method"); }
publicvoidmethodB() { System.out.println("This is Parent B Method and I am Calling Method A"); methodA(); } }
publicclassChildClass { privateParentClassparentClass=newParentClass(); publicvoidmethodA() { System.out.println("This is Child A Method and I am Calling Parents Method A"); parentClass.methodA(); }
publicvoidmethodB() { System.out.println("This is Child B Method and I am Calling Parents Method B"); parentClass.methodB(); } }