코드에서 다중 상속의 개념과 virtual
키워드를 사용한 상속 문제 해결에 대해 설명합니다. 각 예제 케이스를 하나씩 분석해 보겠습니다.
#pragma once
#include <iostream>
namespace case1
{
class BaseA
{
public:
int m = 0;
void foo()
{
std::cout << "BaseA" << std::endl;
}
};
class BaseB
{
public:
int m = 0;
void foo()
{
std::cout << "BaseB" << std::endl;
}
};
class Derived : public BaseA, public BaseB
{
};
}
#pragma once
: 헤더 파일의 중복 포함 방지를 위한 전처리기 지시문입니다.namespace case1
: case1
이라는 네임스페이스를 정의하여 해당 코드 범위를 구분합니다.class BaseA
: 기본 클래스 BaseA
입니다. int m
이라는 멤버 변수가 있고, foo()
함수는 "BaseA"를 출력합니다.class BaseB
: 기본 클래스 BaseB
입니다. int m
이라는 멤버 변수가 있고, foo()
함수는 "BaseB"를 출력합니다.class Derived : public BaseA, public BaseB
: Derived
클래스는 BaseA
와 BaseB
를 다중 상속합니다.설명:
Derived
클래스는 BaseA
와 BaseB
를 모두 상속받아 m
과 foo()
함수를 두 번 상속받습니다.Derived
클래스에서 foo()
를 호출할 때 BaseA
의 foo()
를 호출할지 BaseB
의 foo()
를 호출할지 모호합니다.m
또한 두 개가 존재하므로, 어떤 m
을 사용할지 명시해야 합니다.#pragma once
#include <iostream>
namespace case2
{
class Base
{
public:
int m = 10;
Base(int m) : m(m)
{
std::cout << "Base(" << m << ")" << std::endl;
}
};
class BaseA : public Base
{
public:
BaseA() : Base(10)
{
std::cout << "BaseA" << std::endl;
}
};
class BaseB : public Base
{
public:
BaseB() : Base(20)
{
std::cout << "BaseB" << std::endl;
}
};
class Derived : public BaseA, public BaseB
{
public:
Derived()
{
std::cout << "Derived" << std::endl;
}
};
}
namespace case2
: case2
라는 네임스페이스를 정의하여 해당 코드 범위를 구분합니다.class Base
: 기본 클래스 Base
입니다. m
이라는 멤버 변수를 초기화하는 생성자가 있습니다. 생성자에서 "Base(m)"을 출력합니다.class BaseA : public Base
: Base
를 상속하는 BaseA
클래스입니다. BaseA
의 생성자에서 Base(10)
을 호출하고, "BaseA"를 출력합니다.class BaseB : public Base
: Base
를 상속하는 BaseB
클래스입니다. BaseB
의 생성자에서 Base(20)
을 호출하고, "BaseB"를 출력합니다.class Derived : public BaseA, public BaseB
: Derived
클래스는 BaseA
와 BaseB
를 다중 상속합니다. 생성자에서 "Derived"를 출력합니다.설명:
Derived
객체를 생성할 때 Base
생성자가 두 번 호출됩니다. BaseA
와 BaseB
가 각각 Base
를 상속하기 때문입니다.Base
의 멤버 변수 m
이 두 개 존재하며, 모호성 문제가 발생합니다.virtual
키워드를 사용한 가상 상속#pragma once
#include <iostream>
namespace case3
{
class Base
{
public:
int m = 10;
Base(int m) : m(m)
{
std::cout << "Base(" << m << ")" << std::endl;
}
};
class BaseA : virtual public Base // virtual을 써서 Base를 한 개만 만든다
{
public:
BaseA() : Base(10)
{
std::cout << "BaseA" << std::endl;
}
};
class BaseB : virtual public Base // virtual을 써서 Base를 한 개만 만든다
{
public:
BaseB() : Base(20)
{
std::cout << "BaseB" << std::endl;
}
};
class Derived : public BaseA, public BaseB
{
public:
Derived()
: Base(30) // Base를 virtual 상속을 한다면 Base의 어떤 생성자를 호출할지 지정해줘야 한다
{
std::cout << "Derived" << std::endl;
}
};
}
namespace case3
: case3
라는 네임스페이스를 정의하여 해당 코드 범위를 구분합니다.class Base
: 기본 클래스 Base
입니다. m
이라는 멤버 변수를 초기화하는 생성자가 있습니다. 생성자에서 "Base(m)"을 출력합니다.class BaseA : virtual public Base
: Base
를 가상 상속하는 BaseA
클래스입니다. 생성자에서 "BaseA"를 출력합니다.class BaseB : virtual public Base
: Base
를 가상 상속하는 BaseB
클래스입니다. 생성자에서 "BaseB"를 출력합니다.class Derived : public BaseA, public BaseB
: Derived
클래스는 BaseA
와 BaseB
를 상속받습니다. Base
의 가상 상속을 명시적으로 지정한 생성자를 호출합니다. 생성자에서 "Derived"를 출력합니다.설명:
virtual
키워드를 사용하여 Base
를 가상 상속하면, Derived
클래스에서 Base
객체가 한 번만 생성됩니다.Derived
생성자에서 Base
의 생성자를 명시적으로 호출해야 합니다. 이는 가상 상속 시 생성자 호출을 명확히 지정하는 역할을 합니다.virtual
키워드를 사용해 다중 상속의 중복 생성 문제를 해결하고 모호성을 제거할 수 있습니다.#include <iostream>
#include "case1.h"
#include "case2.h"
#include "case3.h"
using namespace std;
// 다중상속은 정말 필요하지 않으면 사용하지 말자
int main()
{
case1::Derived d1;
// 어떤 부모의 것을 호출해야할지 모호하다
//d1.foo();
//d1.m;
d1.BaseA::foo();
d1.BaseA::m;
d1.BaseB::foo();
d1.BaseB::m;
cout << endl;
case2::Derived d2; // Base 생성자 두 번 호출
// BaseA 부모의 m인지 BaseB 부모의 m인지 모호하다
//cout << d2.m;
cout << d2.BaseA::m << endl;
cout << d2.BaseB::m << endl;
cout << endl;
case3::Derived d3;
// Base가 한 개만 만들어지기 때문에 모호하지 않다
cout << d3.m << endl;
}
case1::Derived d1;
: case1
의 Derived
객체를 생성합니다. BaseA
와 BaseB
의 모호성 문제로 인해 foo()
나 m
에 바로 접근할 수 없습니다.d1.BaseA::foo();
, d1.BaseB::foo();
: 명시적으로 BaseA
와 BaseB
의 foo()
를 호출합니다.case2::Derived d2;
: case2
의 Derived
객체를 생성합니다. Base
생성자가 두 번 호출됩니다.cout << d2.BaseA::m << endl;
, cout << d2.BaseB::m << endl;
: m
에 접근할 때 모호성을 피하기 위해 BaseA
와 BaseB
를 명시합니다.case3::Derived d3;
: case3
의 Derived
객체를 생성합니다. Base
객체가 한 번만 생성됩니다.cout << d3.m << endl;
: m
에 접근할 때 모호하지 않습니다. Base
가 한 번만 생성되었기 때문입니다.**결론
:**
virtual
키워드를 사용한 가상 상속으로 이러한 문제를 해결할 수 있습니다.