본문 바로가기

카테고리 없음

C++의 동적 바인딩

소프트웨어 공학수업을 들으면서 객체지향에 관해 이야기가 나오게 되었는데, 동적 바인딩이 무엇인가에 대한 질문을 받았다. 개념적인 의미로는 실행 시간에 메소드를 결정하여 실행시키는 것으로 알고 있는데 막상 예시를 들어 설명하려니 쉽사리 말로 나오지 않았다.

그래서 이 기회에 정리하면 좋겠다는 생각에 글을 써본다.

위의 내용대로 동적바인딩의 의미는 알았다. 반대되는 성질로 정적바인딩도 있으며, 컴파일 단계에서 실행할 메소드를 결정하게 된다. 무슨말인지 예시를 통해 알아보자

class Animal
{
		public:
		virtual void showInfo() = 0; //순수 가상함수, 반드시 재정의
}
class Dog : public Animal
{
		public:
		void showInfo(){ print("강아지"); }
}
class Cat : public Animal
{
		public:
		void showInfo(){ print("고양이"); }
}

Animal* animal;
Dog* dog;
dog = new Dog();
dog->showInfo();    //정적 바인딩, 컴파일 단계에 실행할 메소드가 결정됨
animal = new Dog(); //동적 바인딩, 실행할 때 메소드가 결정됨
animal->showInfo();
animal = new Cat();
animal->showInfo();
/*
출력: 
강아지
강아지
고양이
*/

dog 클래스의 포인터는 인스턴스 객체로 Dog를 받기 때문에 컴파일 단계에 실행될 메소드가 결정된다.

하지만 animal 클래스의 포인터는 인스턴스 객체로 Dog, Cat을 받기 때문에 프로그램 입장에서 어떤걸 실행할 지 모른다. 따라서 실행될 때!! 메소드를 결정하게 된다.

그렇다면 이런 질문이 있을 수 있다. "이런걸 사용하면 어떤게 좋나요?" 또다른 예시를 통해 살펴보자~

class Member
{
		private:
			int id;
			string name;
		public:
			int GetId() {return id;}
			string GetName() {return name;}
			void SetId(int id) {this->id = id;}
			void SetName(string& name) {this->name = name;}
}
class MemberRepository //Member를 저장하기 위한 객체
{
		protected:
			Connector* con = 0; //데이터를 저장하는 구현체 (메모리 혹은 DB)
		public:
			virtual void save(Member& member) = 0; //순수 가상함수
}
class MemoryMemberRepository : public MemberRepository
{
		public:
			MemoryMemberRepository(Connector* con) {this->con = con;}
			void save(Member& member){con->execute(member);} //메모리에 저장
}
class DatabaseMemberRepository : public MemberRepository
{
		public:
			DatabaseMemberRepository (Connector* con) {this->con = con;}
			void save(Member& member){con->execute(member);} //데이터베이스에 저장
}

MemberRepository* memberRepository;
memberRepository= new MemoryMemberRepository(con); //코드 유지보수에 용이
//memberRepository= new DatabaseMemberRepository(con);
memberRepository->save(member);

내가 개발단계에서 아직 메모리에 데이터를 저장하게 될 지, 데이터베이스에 저장하게 될 지 모르는 상태이다. 바로 그럴 때 각각을 추상화하여 MemberRepository라는 객체를 만들고 동적 바인딩 개념을 사용하여 메모리 혹은 데이터베이스 저장 객체를 갈아끼워 줄 수 있다는 점이다. 이런식으로 구현한다면 작업에 병목이 없으며, 추후에 파일에 저장이 된다해도 별도의 파일 저장 객체를 구현하여 갈아끼워주면 되기 때문에 유지보수가 용이하다!

 

C++ 기준, 동적 바인딩과 같이 가상함수라는 개념이 따라오게 된다.

가상함수는 순수 가상함수와 일반 가상함수로 나뉘는데, 좀 더 파고들어 가상함수를 선언하게 되면, 가상함수 테이블을 가리키는 포인터가 메모리에 4바이트가 추가된다. 하지만 그런 것보단 왜 굳이 일반과 순수 두 가지로 나눈 의미에 대해 집중해보자

나의 생각으로는 일반 가상함수는 부모가 가진 특성을 상속받아 코드의 재사용성을 높이기 위한 방법으로 사용하고, 순수 가상함수는 반드시 구현해야할 구현체로 규약의 의미를 갖는 방법으로 사용하면 좋을거라 생각한다.