본문 바로가기

Programming

기초지식 정리

가상함수
- 어떤 메소드를 실제로 호출하는 객체가 어떤 클래스인지에 따라 구현이 결정되는 메소드를 뜻한다.

class A {
public:
virtual void print() { cout << "A"; }
};

class B : A{
public:
virtual void print() { cout << "B"; }
};

class C : B {
public:
virtual void print(){ cout << "C"; }
};

실행시에 쓰이는 클래스형( new 다음에 오는 클래스형 )에 따라 호출되는 메소드가 결정된다.
A* a = new C();
B* b1 = new B();
B* b2 = new C();
a->print();  // "C"
b1->print(); // "B"
b2->print(); // "C"

* 가상함수의 장점 :
- 1. 실행시에 메소드를 선택할수 있다는 점.
- 2. 추상메소드로 사용되며 다형성을 구현할수 있다는 점.
class Bird{
public:
virtual void fly() = 0;
};

class Bidulgi : Bird {
public:
virtual void fly(){  cout << "비둘";  }
};

Bird* bird = new Bidulgi();
bird->fly();

* 가상함수의 단점 : 별도의 메모리의 사용, 오버헤드
- virtual 이란 키워드가 한개라도 붙은 클래스의 객체를 선언할경우, 그 클래스만을 위한 vtable( virtual 테이블 ) 이 하나 생긴다. 
  객체가 만들어질때 객체의 맨 앞 4byte는 virtual 테이블의 포인터가 저장된다.
  virtual 함수 호출시마다 테이블을 참조해야 하므로 부하가 있다.

가상소멸자를 사용하는 이유

class A {
public:
~A(){ a = 0; }
int a;
}

class B : public A{
public:
~B(){ b = 0; }
int b;
};

class C : public B{
public:
~C(){ c = 0; }
int c;
};

A* obj = new C();
delete obj;

- 위의 경우 A클래스의 소멸자만 호출된다. B,C 클래스 부분의 데이터는 초기화 되지 않는다!


class A {
virtual ~A(){ }
}
class B : public A{
virtual ~B(){ }
};
class C : public B{
virtual ~C(){ }
};

A* obj = new C();
delete obj;

- 위의 경우 obj 는 먼저 A클래스를 본다. 그래서 봤더니 소멸자가 virtual 이라,
B클래스로 본다-> B클래스도 또 virtual 이라 C 클래스를 본다.
결국 C 소멸자->B 소멸자 -> A 소멸자 순으로 소멸자를 호출한다.
A* 를 가지고 객체를 지울경우 virtual 로 하지 않으면, 정확한 데이터 초기화가 이루어 지지 않아
메모리 누수가 발생할수 있다.
즉 상속을 이용한 경우, 소멸자는 virtual 로 해줘야 한다.


오버로딩
- 한 클래스에 동일한 이름의 함수를 중복 정의하는 것.

- 이름이 같은 함수에 전달인자의 크기가 다르거나, 전달인자 타입이 다른 경우 오버로딩 허용
- 이름과 전달인자 개수, 전달인자의 타입이 모두 같은 경우는 오버로딩 불가

class D{
public:
void test( int a );
void test( int a, int b );  // 추가 시 error
int test( double a );
int test( int a, int b );
};

오버라이딩
- 상속구조의 부모클래스로 부터 자식클래스가 함수를 재정의 한것
class Bird{
public:
virtual void swing() = 0;
};

class Bidulgi : Bird {
public:
virtual void swing(){  cout << "퍽퍽";  }
};

class Doksuri : Bird {
public:
virtual void swing() { cout << "훅훅"; }
}

다형성
- 부모 클래스의 형 포인터를 이용해 하위 객체들을 참조 할 수 있게 해주는 개념.
- 함수 swing() 이름은 같지만 새 객체에 따라 어떻게 날개짓을 하는지 따로 정의하고
Bird* b = new Doksuri;
b->swing(); // "훅훅"
호출할때 swing() 이라는 함수가 가상함수라면, 다형성이라는 개념이 적용된다.
이 개념은 객체의 실제 형에 우리가 크게 신경쓰지 않아도 된다는 장점이 있다.

포함 vs 상속
- 상속을 하는 경우 : is-a 관계가 성립될때
- 포함을 하는 경우 : has 관계

적 클래스와 무기 클래스가 있을때 상속을 하게 되면 "적은 무기이다?" 이상해진다.
적 클래스가 무기를 '가진다' 라고 말하며, 이때 포함관계를 갖는게 명확하다.

상속
- 부모클래스의 함수와 데이터를 제공받는것. 더 특화된 버전의 클래스를 위한 행동을 제공할 수 있게 해준다.
class Shape {
int center; // 중심점
}
class Rectangle : public Shape {
int h; // 세로
int w; // 가로
};

class Ellipse : public Shape{
int a; // 긴 반지름
int b; // 짧은 반지름
};

다중상속
- 한 클래스에서 하나 이상의 클래스를 상속할때 이를 다중 상속이라 부름.

다중상속의 문제점 & 해결방안
class A{
protected:
bool flag;
};

class B : public A{ };
class C : public A{ };
class D : public B, public C {
public:
void setFlag( bool bflag ){ flag = bflag; }
};

다중상속중에 문제가 발생할수 있는 경우는 일명 다이아몬드 상속의 경우이다.
이렇게 다중상속을 하게 될경우, B와 C클래스에 각각 flag 라는 변수가 생긴다.
flag = bflag; 하게 되면 이때 flag가 B클래스의 것인지, C클래스의 것인지 불분명해져서
컴파일시 오류가 발생하게 된다.
한가지 방법으로는 B::flag = bflag; 이런식으로 해결한다.

위와 같은 상속은 메모리상에 flag 가 2개가 잡히게 되는데
이문제를 해결하기 위해 C++ 은 가상상속이라는 새로운 개념을 도입하였다.
다이아몬드 상속 계층이 만들어지더라도 자식 개체 구조 내에 한번씩만 부모 클래스가 포함되도록 해준다.

프로그래밍의 종류
일반프로그래밍이란 ? STL 을 이용한 제네릭한 프로그램을 구현하는 패러다임을 말한다.
객체지향프로그래밍이란? 클래스를 사용하여 객체 중심적으로 생각하여 프로그램을 구현

'Programming' 카테고리의 다른 글

리눅스 사용자 관련 명령어  (0) 2011.03.03
CPPUNIT 테스트  (0) 2011.01.07
IOCP 정리  (0) 2010.12.02
SELECT 함수 정리  (0) 2010.11.30
리눅스 SIGPIPE 처리  (0) 2010.11.29