C++로 특정 Class를 등록하고 필요할때 마다 사용하고 싶은데..

fleabane의 이미지

안녕하세요 C++ 초보입니다.

프로그램을 짜다가 특정 Class를 어딘가에 등록을 하여 필요 할때마다
끄내쓰거나 바꿔쓰거나 하고 싶은 마음에 아래와 같이 구현을 해보았습니다.

//Interface Class
class IDev
{
public :
   IDev();
   virtual ~IDev();
 
   virtual void executeA() = 0;
   virtual void executeB() = 0;
}

그래서 이놈을 상속 받는 TestDevice를 구현했습니다

//Test Device
class CTestDevice : public IDev
{
public :
   CTestDevice();
   virtual ~CTestDevice();
 
   virtual void executeA() { printf("Execute A\"); }
   virtual void executeB() { printf("Execute B\"); }
}

뭐 딱 보면 아시겠지만 거의 동일한 기능을 가진 Device를 Class로 각각 구현을 하여 동일한
인터페이스를 가지고 갈려고 하는 것입니다.

그래서 이놈을 등록 시킬 수 있는 Class 하나를 만들어서

class CDevRegsiter
{
private :
   static IDev* m_pDevice;
 
public :
   static void regsiterDevice( IDev* pDev )
   {
      if( m_pDevice )
      {
          m_pDevice = new???????????  /*이부분을 어떻게 하면 될지???*/
      } 
   }
   static IDev* getDevice()
   {
       return m_pDevice;
   }
}
 
void main()
{
   CTestDevice testDev;
 
   CDevRegsiter::regsiterDevice( &testDev );
 
   CDevRegsiter::getDevice()->executeA();
 
}

위에 CDevRegsiter::regsiterDevice()안에 ?????? 한부분이 고민입니다.

그냥 m_pDevice = pDev 해도 뭐 컴파일 에러는 당연히 안나더군요.
하지만 결국 그렇게 되면 CDevRegsiter::m_pDevice는 나중에 쓰레기 값이 들어가기 때문에 안될거 같고

main에서

CTestDevice gTestDev;
void main()
{
   CDevRegsiter::regsiterDevice( &gTestDev );
 
   //이거는 밑에것이랑 같아서 의미 없다고 생각됨
   CDevRegsiter::getDevice()->executeA();
   gTestDev.executeA();
}

이렇게 하자니 그럼 굳이 Interface class를 만들 필요도 없을거 같고..

파리마터로 들어온 Class현재의 바인딩(이 표현이 맞는지 모르겠습니다)된 class가 뭔지 알수 있는 방법은 없을까요?

익명 사용자의 이미지

void regsiterDevice( IDev* pDev )에서
pDev가 IDev에서 상속된 놈인지 검사해 주는 걸 이미 하고 있으며...
그냥 m_pDevice = pDev 하면 될 듯...

결국 그렇게 되면 CDevRegsiter::m_pDevice는 나중에 쓰레기 값이 들어가기 때문?????? 이게 무슨 말인지????

fleabane의 이미지

예를 들어

void setDevice()
{
   CTestDev testDev;
 
   CDevRegsiter::regsiterDevice( &testDev);
}
 
void runDevice()
{
   CDevRegsiter::getDevice()->executeA();
}
 
void main()
{
 
   setDevice();
   runDevice();
 
}

이런식으로 구현을 했을때에는 CDevRegsiter::m_pDevice는 setDevice()안의 로컬 testDev의 주소를 대입했기 때문에
runDevice()에서 사용할때에는 해당 주소에 결국 쓰레기 값만 있게 될거란 설명이었습니다.

static void regsiterDevice( IDev* pDev )
   {
      if( m_pDevice )
      {
          m_pDevice = new???????????  /*이부분을 어떻게 하면 될지???*/
      } 
   }

그럼 이부분에 대입을 할때 operator= 을 overloading을 해서 하면 문제가 해결될까요?
IDev = TestDev꼴이 되는거라서 이게 가능한지 헛갈리네요.

익명 사용자의 이미지

인자로 넘겨주는 인스턴스가 소멸되었을때 얘기라면 대입이 아니라 복사를 하면 될듯...

klara의 이미지

C++의 클래스는 스스로를 서술하는 기능을 포함하지 않습니다.
표준에서 보장하는 것은 상속관계를 dynamic_cast로 확인할수 있는 정도입니다.
말씀하신대로 등록하고 싶은 것은 클래스이므로, 암만 객체를 넘겨줘봤자 소용이 없습니다.
클래스에 대한 정보를 넘겨야지요.
클래스를 등록해서 어떤 것을 가능하게 할 것인지 구체적으로 밝히지 않으셨으므로 구체적인 답변은 불가능합니다만,
클래스에 대한 정보를 담은 객체(메타객체)를 넘겨주거나, 객체 자체에 메타 객체를 포함시켜야 겠지요.
예를 들어 이름만 알면 된다면,
class MetaDevice {
public:
virtual std::string name() const = 0;
};
이런 클래스를 만들어서, 이걸 상속 받아서 이걸 넣어줘야겠지요.
class MetaDeviceA : public MetaDevice {
public:
virtual std::string name() const {return "device a";}
};
이런 메타객체는 보통 여러개의 객체(이경우라면 많은 device a 객체들)에 대해서 하나만 필요하므로, 싱글톤으로 만드는 것도 생각해 볼수 있을듯합니다.
런타임에 등록된 클래스의 생성이 필요하다면 팩토리 패턴을 이용할수도 있겠구요.

익명 사용자의 이미지

위 경우는 void regsiterDevice( IDev* pDev )라고 할때
pDev를 넘겨주면 IDev *인지 타입검사를 하면서 상속관계가 검사됩니다.
아니면 IDev *로 캐스팅이 안되면 컴파일 오류...

필요이상으로 어렵게 생각하는듯...

fleabane의 이미지

제가 설명이 좀 부족했던 부분이 있군요.

예를 좀 정확히 들어서 설명을 드릴테니 아시는 한도내에서 답변 해주시면 감사하겠습니다.

말씀하신데로 거의 동일한 기능을 수행하는 Device를 필요한 상황에 따라 런타임에 등록을 하고 해지를 하며 사용할려고
생각중입니다.

그 Device를 예를 들어 뭐 마우스의 클릭과 관련된 디바이스라고 하면 사용자가 보기에는 마우스는
Left/Right Click의 동일한 기능만 가지고 있으나 그 Left Click/ Right Click을 동작하는 방법은 내부적으로
차이가 있을 수 있다는 전제로 구상을 하였습니다.

사용자는 Mouse인스턴스의 Left/Right Click을 사용하지 그 내부 구조는 모르고 싶다는 전제이고요.

//Mouse Interface Class
class IMouse
{
public : 
   virtual void leftClick() = 0;
   virtual void RightClick() = 0;
};
 
//Char Mouse Device
class CCharMouse : public IMouse
{
private :
   char m_mouseData;
   void clearData(){ m_mouseData = 0; }
 
public : 
   virtual void leftClick() { m_mouseData = 1; }
   virtual void rightClick() 
   {
      m_mouseData = 2; 
      clearData(); 
   }
}
 
 
//Int Mouse Device
class CIntMouse : public IMouse
{
private : 
   int m_mouseData;
   void resetData() { m_mouseData = 0; }
 
public :
   virtual void leftClick() { m_mouseData = 1; }
   virtual void rightClick() 
   { 
      m_mouseData = 2; 
      resetData();
   }
}

이렇게 되었을때

void CDevRegsiter::regsiterDevice( IMouse* pMouse )
{
    m_pDev = pMouse;
}

결과적으로 Device는 동일한 method를 제공하지만 각 Device별로 동작하는 방식이 다르고 사용하는 Data와 private 맴버가
다른 상태인 상황에서(위에는 간단하게 char/int 하나씩 했지만 실제로는 더 큰 Data Table을 사용하여 진행이될때)
register내부에서 '='연산(Deep Copy)가 가능한지가 궁금합니다.

지금 operator=을 overloading해보고는 있는데 컴파일 에러 잡고 있는 중이라서 아직 결과는 안나온 상태입니다.

klara의 이미지

무슨 의도이신지 잘 모르겠습니다.
앞의 설명보다, 저렇게 등록한 걸로 무엇을 할려고 하시는 것인지를 구체적으로 보여주시는게 더 알기 쉬울 것같습니다.
적으신걸 보니 또 제가 앞에 적은거랑은다른 의도이신것도 같고...

아무튼 맨뒤에 질문하신것에만 답변하자면,
일단 overloading은 컴파일타임에 결정되는 것이므로 런타임에 동적바인딩되게 할수는 없습니다(이게 되게 하는게 overriding이지요).
더불어서 IMouse *operator = (IMouse *)와 IMouse &operator = (const IMouse &)는 다른 함수입니다.
다형성과 함께 deep copy가 필요하다면 copy 함수를 가상함수로 넣어두면 되겠지요.

fleabane의 이미지

참 머리속에 있는걸 글로 풀어 쓰는게 쉬운게 아닌군요 ㅎㅎ

위와 같이 mouse class가 있을때

//'Process A'동작
void ProcA()
{
    char type;
    IMouse* pMouse;
 
    while(1)
    {
        //새로운 마우스 타입이 들어오기를 기다린다.
        type = checkMouse();
        if( type == 'c' )
        {
            //Mouse를 체크하다가 Char Type이 들어오면 CharMouse Deivce를 생성한다.
            pMouse = new CCharMouse();
        }
        else if( type == ' i' )
        {
            //Mouse를 체크하다가 Int Type이 들어오면 IntMouse Deivce를 생성한다.
            pMouse = new CIntMouse();
        }
        //Mouse Device를 등록한다
        CDevRegsiter::regsiterDevice( pMouse  );
 
        delete pMouse;       
    }
}
 
 
 
//Process B 동작
void ProcB()
{
    IMouse *pMouse;
    while(1)
    {
        //시작신호를 기다린다
        waitTrigger();
 
        //신호가 들어오면 현재 등록된 마우스 동작을 시킨다
        pMouse = CDevRegsiter::getDevice();
 
        pMouse->leftClick();
        pMouse->rightClick();
    }
}

대충 시나리오는 이렇습니다.

'Process A'에서 지속적으로 현재 마우스 타입을 검사하며 마우스 타입이 변경이 되었을때
해당 정보를 CDevRegsiter에 등록을 시켜주면

'Process B'에서 트리거 신호를 기다렸다가 트리거 신호가 들어오면 현재 등록되어 있는 마우스의
left/right 클릭을 실행하는 것이죠.

class CDevRegsiter
{
private :
   static IMouse* m_pMouse
 
public :
   static void regsiterDevice( IMouse* pDev )
   {
      if( m_pMouse)
      {
          m_pMouse= new???????????  /*이부분을 어떻게 하면 될지???*/
      } 
   }
   static IMouse* getDevice()
   {
       return m_pMouse;
   }
}

그래서 CDevRegister Class에 현재 정보를 저장하려고 IMouse* m_pMouse맴버 변수를 static으로 해두었습니다.
IMouse m_mouse로 하고 싶다만 virtual pure class라 그렇게는 불가능하고..

그래서 regsiterDevice( IMouse* pDev )에는 CCharMouse가 들어올수도 있고 CIntMouse가 들어올 수도 있는 상황인데

m_pMouse= new???????????  /*이부분을 어떻게 하면 될지???*/ 

에 대한 의문이 생긴거고요.

klara의 이미지

마우스 객체를 넘겨주고 해제 하지말고, CDevRegister 클래스가 필요없어진 마우스 객체의 메모리를 해제하게 하면 되겠네요.

IMouse *CDeveRegister::m_pMouse = 0;

void CDevRegister::registerDevice(...) {
if (m_pMouse) {
delete m_pMouse;
m_pMouse = 0;
}
m_pMouse = pDev;
}

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.