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