프로그래밍 공부
작성일
2023. 5. 25. 20:28
작성자
WDmil
728x90

class에서 순수가상화 class를 사용하여, 인터페이스를 구축할 수 있다.

 

순수 가상화 선언 시 단독사용이 안되고, 필요할 시 자식class로 호출해야한다.

class IErrorLog // 순수 가상함수로만 이루어진 인터페이스 이다.
{
public:
	virtual ~IErrorLog() = default;
	virtual bool ReportError(const char* const error) abstract; // 순수 가상화 선언
};

class FileErrorlog : public IErrorLog
{
public:
	virtual bool ReportError(const char* const error) override
	{
		cout << "writting error to a file" << endl;
		return true;
	}
};

class ConsoleErrorlog : public IErrorLog
{
public:
	virtual bool ReportError(const char* const error) override
	{
		cout << "Console error to a input" << endl;
		return true;
	}
};

void DoSomething(IErrorLog& log)
{
	log.ReportError("Error");
}

int main()
{
	//IErrorLog f;
	FileErrorlog fileLog;
	ConsoleErrorlog consoleLog;

	DoSomething(fileLog);
	DoSomething(consoleLog);
	return 0;
}

위와같이, 원하는 error로 타입을 변환할 수 있다.


이러한 상속에서 다이아몬드 상속 문제가 발생할 수 있다.

 

다이아몬드 상속문제는, 상속 시 이중상속이 한번 일어나고, 바로 다음 상속이 그 두개의 상속을 받아올 때 발생하는데,

 

제일 윗 단계의 값을 가져올 때 모호성 문제가 발생하는걸 이야기한다.

 

다음 코드는 이러한 다이아몬드 상속문제를 다룬다.

#include <iostream>

using namespace std;

/*
	다이아몬드 상속 문제
	A
   / \
  B   C
   \ /
    D

	class A {};
	class B : public A {};
	class C : public A {};
	class D : pulbic B, public C {};

	위와같이 상속이 되면, D는 B와 C중 어떤걸 사용해야 할 지 알수가 없어져서 모호성 문제가 발생함.
*/

class PoweredDevice
{
public:
	PoweredDevice(int power) { cout << "PowerdDevice" << power << endl; }
	int i;
};

class Scanner : virtual public PoweredDevice
{
public:
	Scanner(int scanner, int power)
		: PoweredDevice(power)
	{
		cout << "Scanner : " << scanner << endl;
	}
};

class Printer : virtual public PoweredDevice
{
public:
	Printer(int printer, int power)
		: PoweredDevice(power)
	{
		cout << "Printer : " << printer << endl;
	}
};

class Copier : public Scanner, public Printer
{
public:
	Copier(int scanner, int printer, int power1, int power2, int power3)
		: Scanner(scanner, power1)
		, Printer(printer, power2)
		, PoweredDevice(power3)
	{}
};

int main()
{
	Copier copier(1, 2, 3, 4, 5);

	cout << &copier.Scanner::PoweredDevice::i << endl;
	cout << &copier.Printer::PoweredDevice::i << endl;

	copier.i; // abiguous 모호성 문제가 생겨난다.
	// 이걸 막기 위해 virtual을 사용해서 가상상속을 해줄 수 있다.
	return 0;
}

다운캐스팅 시, 값이 변동될 수 있다.

 

static _cast를 사용하면, 변수값이 원하지 않더라도 변경될 수 있다.

class에 저장된 데이터공간에 맞게 잘라서 박아버리기 때문에, int형 k 와 int 형 j 두개가 있을 경우, 두개의 변수 명이 다르더라도 그 변수위치에 삽입된다는 이야기이다.

 

다음은 그러한 코드의 예시이다.

#include <iostream>

using namespace std;
/*
	dynamic cast
	- 안전한 다운캐스팅 에 사용

*/

class Base
{
public:
	int i = 0;
	virtual void Print() { cout << "Base" << endl; }

};

class Derived1 : public Base
{
public:
	int j = 1;
	virtual void Print() override { cout << "Derived1" << endl; }
};

class Derived2 : public Base
{
public:
	int k = 2;
	virtual void Print() override { cout << "Derived2" << endl; }
};

void DoSomething(Base* b)
{
	//Derived2* baseToD2 = static_cast<Derived2*>(b);
	//baseToD2->k = 888;
	// 위와같은 방식으로 진행하면 j 에 888이 들어가버린다.
	Derived2* baseToD2 = dynamic_cast<Derived2*>(b);
	baseToD2->k = 888;
	// 런타임 상황에서 잡아준다.
}

int main()
{
	Derived1 d1;
	Base* base = &d1;

	Derived1* baseToD1 = static_cast<Derived1*>(base);
	baseToD1->Print();

	DoSomething(&d1);
	// j의 정보가 있는 d1을 d2로 바꾸어서 k값을 바꾸었다.

	cout << d1.j << endl;
	// 강제적인 형변환이 일어나기 때문에, 원하지 않는 정보의 변환이 이루어질 수 있다.

	// 다이나믹 케스팅은 런타임 때 오류를 잡아준다. 그래서 조금 느릴 수 있다.

	return 0;
}

 

728x90