다형성을 설계관점을 만든다.

기본클래스에서 자식클래스에게 제약을 걸어준다.

순수 가상 함수
- 자식클래스에서 반드시 오버라이딩 해주어야한다.
- virtual 로 선언되고 마지막이 = 0; 이면 순수 가상 함수
- 별도로 함수를 구현할 수 있다. 호출할 수 없다.

virtual void speak() const = 0;



추상 기본클래스
- 순수 가상함수가 포함이된 클래스
- abstact class는 인스턴스생성이 안된다.

class Animal
{
protected:
	string m_name;

public:
	Animal(string name)
		:m_name(name) {}

public:
	string getName() { return m_name; }

	virtual void speak() const = 0;
};


인터페이스
- 순수 가상 함수로만 이루어진 클래스
- 파라미터를 인터페이스로 받으면 다양하게 
구현된 자식 클래스들을 이용할 수 있다.

class IErrorlog
{
public:
	virtual bool reportError(const char * errorMessage) = 0;

	virtual ~IErrorlog() {}
};


순수 가상 함수가 있는 Class를 상속받으면 무조건 오버라이딩해야한다.

 

#include <iostream>
#include <string>

using namespace std;

class Animal
{
protected:
	string m_name;

public:
	Animal(string name)
		:m_name(name) {}

public:
	string getName() { return m_name; }

	virtual void speak() const = 0;
};

//void Animal::speak() const
//{
//	cout << m_name << " ??? " << endl;
//}

class Cat :public Animal
{
public:
	Cat(string name)
		:Animal(name) {

	}

	void speak() const
	{
		cout << m_name << " Meow " << endl;
	}
};


class Dog :public Animal
{
public:
	Dog(string name)
		:Animal(name) {

	}

	void speak() const
	{
		cout << m_name << " Woof " << endl;
	}
};

class Cow : public Animal
{
public:
	Cow(string name)
		:Animal(name) {

	}

	//void speak() const
	//{
	//	cout << m_name << " Woof " << endl;
	//}

};


//Interface 예제
//자신이 정의하지 않고 순수가상함수만 가지고 있다.
//Interface는 대문자 I를 붙여주는게 관습이다.
class IErrorlog
{
public:
	virtual bool reportError(const char * errorMessage) = 0;

	virtual ~IErrorlog() {}
};

class FileErrorLog : public IErrorlog
{
public:
	virtual bool reportError(const char * errorMessage)
	{
		cout << "File Error" << endl;
		return true;
	}

};

//부모클래스를 이용하기에 여러방법이 사용가능하다.
void logTest(IErrorlog & log) {
	log.reportError("Runtime Error!!");
}

int main()
{
	//speak()가 구현이 안되어있다.
	//Cow c("n");

	Cat c("cat");
	c.speak();

	FileErrorLog log;
	logTest(log);

	return 0;
}

'개발 소발 > 개발 C++(기초)' 카테고리의 다른 글

c++ 객체잘림,reference wapper  (0) 2019.09.30
c++ 다이아몬드 상속문제 해결  (0) 2019.09.30
c++ vitual,가상함수  (0) 2019.09.30
c++ 정적,동적바인딩  (0) 2019.09.30
c++ 가상소멸자  (0) 2019.09.23

virtual 함수 (가상함수)는 정적이 아닌 동적으로 실행된다.

함수를 실행시 virtual펑션 테이블(포인터)을 찾고 테이블안에 함수 포인터를 사용한다.


다형성 사용시 자식 클래스에도 virtual펑션 테이블(포인터)가 생긴다.
동적바인딩 될때 오버라이딩된 함수는 자식 테이블 함수 주소 값을 가진다.

virtual을 사용하면 sizeof()로 확인시  포인터 메모리까지 할당되는 걸 볼 수 있다.

 

#include <iostream>

using namespace std;

class Base
{
public:
	virtual void fun1() {};
	void fun2() {};
};

class Der : Base
{
public:
	void fun1() {};
	void fun3() {};

};

int main()
{
	cout << sizeof(Base) << endl;
	cout << sizeof(Der) << endl;
	return 0;
}

가상함수의 내부동작 이해

속도면에선 static,dynamic중에 static이 빠르다.

정적 바인딩은 프로그램이 실행될때 메모리에 배치된다.
동적 바인딩은 그때 그때 배치된다.

 

dynamic바인딩이란? 

쉽게말해 함수 포인터로 동적할당해 그때그때 맞게 함수 주소를 대입한다.


dynamic은 포인터로 주소를 타고 가야한다.
하지만 dynamic 즉 동적바인딩을 쓰면 프로그래밍이 유연해진다.

 

#include <iostream>

using namespace std;

int add(int x, int y) {
	return x + y;
}

int subtract(int x, int y) {
	return x - y;
}

int multiply(int x, int y) {
	return x * y;
}
int main()
{
	int x, y;
	cin >> x >> y;

	int op;
	cout << "0 : add, 1 : subtract, 2 : multiply" << endl;

	cin >> op;
	// statuc binding (early binding)
	/*int result;
	switch (op) 
	{
	case 0: result = add(x, y); break;
	case 1: result = subtract(x, y); break;
	case 2: result = multiply(x, y); break;
	}
	cout << result << endl;
	*/

	//Dynamic binding (late binding)
	int(*func_ptr)(int, int) = nullptr;
	switch (op) {
		case 0: func_ptr = add; break;
		case 1: func_ptr = subtract; break;
		case 2: func_ptr = multiply; break;
	}
	cout << func_ptr(x, y) << endl;
	return 0;
}

+ Recent posts