본문 바로가기

객체지향

객체지향 알아 둘 것

반응형

Q>가상함수 :C++의 가상함수 동작원리는 ?


가상 함수는 'vtable' 혹은 가상 테이블(virtual table)에 의존한다. 어떤 클래스의 함수가 virtual로 선언되어있으면 해당 클래스의 가상 함수 주소를 보관하는 vtable이 만들어진다.  컴파일러는 또한 해당 클래스의 vtable을 가리키는 vptr이라는 숨겨진 변수 (hidden variable)를 해당 클래스에 추가한다.  하위클래스가 상위 클래스의 가상 함수를 오버라이드 하지않으면 하위 클래스의 vtable은 상위 클래스의 가상 함수 주소를 보관한다.  이 vtable을 사용하여 가상 함수가 호출 될때 어느 주소에 있는 함수가 호출되어야하는지를 결정한다. c++의 동적 바인딩은 이가상테이블 메커니즘을 사용하여 실행된다. 

따라서 하위클래스 객체에 대한 포인터를 상위클래스 객체에 대한 포인터에 할당하면, vptr 변수는 하위클래스의 vtavle을 가리킨다. 이렇게 배정되므로 최하위 클래스의 가상함수가 호출되는 것이다 .


Q>얕은복사 vs 깊은 복사 : 얕은 복사(shallow copy)와 깊은 복사 (deep copy)는 어떤 차이가 있는가? 

                                    이 각각을 어떻게 사용할 것인지 설명하라.


얕은 복사는 한 객체의 모든 멤버변수의 값을 다른 객체로 복사한다. 깊은 복사의 경우에는 그뿐 아니라 포인터 변수가 가리키는 모든 객체에 대해서도 시행한다. 

shallow_copy를 사용할 경우 객체 생성과 삭제에 관련된 많은 프로그래밍 오류가 프로그램 실행 시간에 발생할수 있다. 따라서 얕은 복사는 프로그래머 자신이 무엇을 하려고 하는 지 잘 이해하고 있는 상황에서 주의 하여 사용해야한다. 대부분읜 경우, 얕은 복사는 실제 데이터를 복제하지 않고서 복잡한 자료구조에 관한 정보를 전달하고자 할때 사용한다 얕은복사로 만들어진 객체를 삭제할때는 조심해야한다. 

실제로는 얕은 복사는 거의 사용되지 않는다. 대부분의 경우에는 깊은 복사를 사용해야하는데 복사되는 자료구조의 크기가 작을때는 더더욱 그러하다. 


Q>가상 상위 클래스 : 상위 클래스의 소멸자를  virtual로 선언해야 하는 이유는 무엇인가 ?


class Foo{

    public: void f();

};

class Bar:public Foo{

public: void f();

}

Foo *p=new Bar();

p->f();  



p->f() 를 호출하면 Foo::f()가 호출 된다. 왜냐하면 p가 Foo에 대한 포인터이고, f()가 virtual로 선언이 되어있지 않기 때문이다. 

Bar::f()를 호출하려면 f()를 가상함수로 선언해야한다.  소멸자는 메모리와 자원을 반환하기 위해 쓰인다. Foo의 소멸자가 가상소멸자로 선언되어있지 않으면 Foo의 소멸자가 호출 될것이다. p가 Bar 객체를 가리키고 있다고 하더라도 말이다. 

소멸자를 가상메서드로 선언해야하는 이유가 바로 이것 때문이다. 포인터가 가리키는 객체의 실제 소멸자가 호출되도록 보장하고싶은것이다. 




[자바에선]?



Q>오버로딩 vs 오버라이딩 


오버로딩(overloading)

 =  두 메서드가 같은 이름을 갖고있으나 인자의 수나 자료형이 다른 경우를 지칭한다. 

public double compuArea(Circle c) { . . .}

public double compuArea(Square s){ . . .}


오버라이딩(overriding)

= 상위 클래스의 메서드와 이름의 용례가 같은 함수를 하위 클래스에 재정의 하는 것 


public abstract class Shape{


      public void printMe(){

        System.out.println("i am a shape");

      }

      public abstract double computerArea();


}


public class Circle extends Shape{


   private double rad=5;

   public void PrintMe()  {

  System.out.println("i am a circle");

              }

  public double computeArea() {

      return rad*rad*3.15 ;

    }

                                 


}


public class Ambiguous extends Shape{


      private double area =10;

     public double computeArea() {

          return area;

  }



}


public class IntroductionOverriding{

          public static void main(Stirng[] arg){


              Shape[] shape= new Shape[2];

              Circle circle = new Circle(); 

              Ambiguous ambiguous =new Ambiguous ();


               shape[0]=circle;  shape[1]=ambiguous; 

           

            for(Shape s :shapes ) {

                       s.printMe();

                       System.out.println(s.computeArea());


              } 

          }


}



출력 결과 


i am a circle. 

78.75

i am a shape. 

10.0 



Ambiguous는 printMe 그대로, Circle은 재정의 






Q>private 생성자 : 상속 관점에서 생성자를 private라고 선언하면 어떤 효과가 있나??


 생성자가 private로 선언된 class A는 ,A의 private 메서드에 접근이 가능해야한 생성자를 호출할수 있다는 것을 의미한다. 

A가 아닌 A의 private메서드와 생성자에 접근할수 있을까? A의 내부 클래스만이 할수있다.  또한, A가 Q의 내부 클래스라면 Q의 다른 내부 클래스도 호출 할수 있다. 

부분 클래스가 부모의 생성자를 호출할수있기 때문에 이는 상속에 직접적인 영향을  끼친다.  클래스 A는 상속 받을 수 있는데, 자기자신 아니면 부모의 내부 클래스에 의해서만 가능하다. 


Q>final 의 의미 

 

final은 사용되는 문맥에 따라 그 의미가 다르다. 

원시 변수에 적용하면: 해당 변수의 값은 변경이 불가능하다. 

참조 변수에 적용하면: 참조 변수가 힙 내의 다른 객체를 가리키도록 변경할수 없다. 

메서드에 적용하면: 해당 메서드를 오버라이드 할수없다. 

클래스에 적용하면: 해당클래스의 하위클래스를 정의할수 없다. 



출처:코딩 인터뷰 189가지 프로그래밍 문제와 해법 완전분석 (프로그래밍 인사이트 )

반응형

'객체지향' 카테고리의 다른 글

Exception  (0) 2021.06.01
추상화와 인터페이스  (0) 2018.08.23
포인터와 참조  (0) 2018.03.27
가상소멸자  (0) 2018.03.27
가상함수  (0) 2018.03.26