C++ 클래스 오버라이드좀 시켜주세요!

MyAbby의 이미지

아래 글 작성자입니다. 전 글의 소스를 통째로 가져와서 가독성이 떨어져 핵심만 뽑았습니다.

callBack()메서드 오버라이드 시키는 방법 없을까요? 신기하게도 되지 않아요.

컴파일은 g++ -o 파일 소스 -lpthread 로 했습니다.

#include <iostream>
#include <unistd.h>
#include <pthread.h>
 
using namespace std;
 
class Parent
{
	public:
		virtual void callBack()
		{
			cout << "Original callBack() reported this: " << this << endl;
		}
	private:
		pthread_t th = 0;
 
		static void *th_func(void *arg)
		{
			Parent *p = (Parent*)arg;
			cout << "*th_func() reported *arg: " << arg << endl;
			p->callBack();
		}
	public:
		Parent()
		{
			if(pthread_create(&th, NULL, th_func, (void*)this) < 0)
				cerr << "thread not born." << endl;
			else
				cout << "thread has born." << endl;
		}
		~Parent()
		{
			if(th!=0)
				pthread_join(th, NULL);
			cout << "joined. Parent leaving." << endl;
		}
};
 
class Child : public Parent
{
	public:
		void callBack()
		{
			cout << "child overrided." << endl;
		}
		Child() : Parent(){}
};
 
int main()
{
	Child *ch = new Child();
	delete ch;
	return 0;
}
kukyakya의 이미지

살짝 손보고 실행해봤는데 오버라이드한 멤버 함수가 잘 호출됩니다.

쓰레드 관련해서는 클래스 안에 넣는것보다는 외부에서 관리해주시는 것이 더 좋을 것 같습니다.

#include <iostream>
#include <unistd.h>
#include <pthread.h>
 
class Parent
{
  public:
    virtual void callBack()
    {
      std::cout << "Original callBack() reported this: " << this << std::endl;
    }
 
    static void* th_func(void *arg)
    {
      Parent *p = (Parent*)arg;
      std::cout << "*th_func() reported *arg: " << arg << std::endl;
      p->callBack();
      return 0;
    }
 
  public:
    Parent()
    { }
 
    virtual ~Parent()
    { }
};
 
class Child : public Parent
{
  public:
    virtual ~Child()
    { }
 
    void callBack()
    {
      std::cout << "child overrided." << std::endl;
    }
 
    Child()
      : Parent()
    { }
};
 
int main()
{
  Parent *ch = new Child();
 
  pthread_t th;
  if(pthread_create(&th, NULL, &Parent::th_func, ch) < 0)
    std::cerr << "thread not born." << std::endl;
  else
    std::cout << "thread has born." << std::endl;
 
  if(th)
    pthread_join(th, NULL);
  std::cout << "joined. Parent leaving." << std::endl;
 
  delete ch;
  return 0;
}
gilgil의 이미지

처음 코드에서 ch 객체를 delete하기 이전에 sleep(1)로 약간의 delay를 줘 보세요. "child overrided."라는 메세지가 제대로 찍힙니다.

int main()
{
	Child *ch = new Child();
	sleep(1);
	delete ch;
	return 0;
}

혹은 Child::~Child() 함수에서 pthread_join을 호출해도 "child overrided."가 뜹니다.

본 현상이 발생하는 원인은 다음과 같습니다.
Parent 클래스의 ctor와 dtor 수행시에는 callBack()이 Parent::callBack()을 가리키게 된다.
Child 클래스의 ctor와 dtor 수행시에는 callBack()이 Child::callBack()을 가리키게 된다.

처음 코드에서 ch 객체를 해제하게 되면 다음과 순서로 작동합니다.
1. Child 클래스 dtor 수행(코드상으로 없으므로 무시)
2. Parent 클래스 dtor 수행(이 경우 callBack은 Parent::callBack()을 가리키게 된다).
3. Parent 클래스 dtor가 수행하고 있을 때 pthread_join이 불리게 되며 th_func의 p->callBack()이 호출될 때에는 Parent::callBack()이 호출되게 된다.

ctor 및 dtor가 호출이 될 때에 VMT(virtual member table)가 바뀌는 시점에 대해 알아 보시기 바랍니다.
자세한 사항은 아래에서 확인해 보세요.
http://www.gilgil.net/10409
http://www.gilgil.net/3311
http://www.gilgil.net/10409

shint의 이미지


이것이 클래스 오버라이드라고 하는데요.
http://iopoi3.blog.me/130152756623

이거 그대로 따라해보시는것도 좋을거 같습니다.

그리고. 만드는 구조를 다른 방식으로도. 고민해보셔야 할거 같습니다.

//Dev C++에서 테스트 해봤습니다. 
 
//http://iopoi3.blog.me/130152756623
//클래스 오버라이드는 자식을 바꾸기 위한 방식이네요. 
//http://iopoi3.blog.me/130152756623
//Parent를 사용하고 싶으면. virtual을 빼면 되네요.  
 
#include <cstdlib>
#include <iostream>
 
#include <process.h>                 //_beginthreadex
 
using namespace std;
 
 
class Parent
{
	public:
		virtual void callBack()
		{
			cout << "Original callBack() reported this: " << this << endl;
		}
	private:
        unsigned int id;
        static unsigned int __stdcall td(void* arg)
        {
			Parent *p = (Parent*)arg;
			cout << "[실행전] *th_func() reported *arg: " << p << " / " << arg << endl;
			p->callBack();
			cout << "[실행후] *th_func() reported *arg: " << p << " / " << arg << endl;
        }
 
//		pthread_t th = 0;
		static void *th_func(void *arg)
		{
			Parent *p = (Parent*)arg;
			cout << "*th_func() reported *arg: " << arg << endl;
			p->callBack();
		}
	public:
 
		Parent()
		{
//			if(pthread_create(&th, NULL, th_func, (void*)this) < 0)
//				cerr << "thread not born." << endl;
//			else
//				cout << "thread has born." << endl;
		}
		~Parent()
		{
//			if(th!=0)
//				pthread_join(th, NULL);
//			cout << "joined. Parent leaving." << endl;
		}
 
		void setRun(void* p)
		{
			cout << "setRun: " << p << " / " << this << endl;
            _beginthreadex(NULL, 0, td, p, 0, &id);
		}
 
};
 
class Child : public Parent
{
	public:
		void callBack()
		{
			cout << endl;
			cout << "child overrided." << endl;
		}
		Child() : Parent()
        {
			cout << "Child(): " << this << endl;
        }
};
 
int main()
{
    //1. 클래스 오버라이드 - 사용 방법.
    //http://iopoi3.blog.me/130152756623
    Child ch;
    Parent* p = new Parent();
    p->setRun(&ch);
	p = &ch;
	p->callBack(); //Parent에 virtual이 붙을경우 이런 방식으로 사용하면 안되네요. 
 
//2. 사용하신 방법 
//	Child *ch = new Child();
//	delete ch; 이 코드 때문에 죽습니다. 스레드 완료후 실행 되야 할거 같습니다. 
 
    system("PAUSE");
    return EXIT_SUCCESS;
}
 
 
/*
//이건 테스팅 방법이 잘못된거 같습니다.
//스레드에 인자값으로 Child의 this가 들어가야 합니다.
 
#include <cstdlib>
#include <iostream>
 
#include <process.h>                 //_beginthreadex
 
using namespace std;
 
 
class Parent
{
	public:
		virtual void callBack()
		{
			cout << "Original callBack() reported this: " << this << endl;
		}
	private:
        unsigned int id;
        static unsigned int __stdcall td(void* arg)
        {
			Parent *p = (Parent*)arg;
			cout << "[실행전] *th_func() reported *arg: " << p << " / " << arg << endl;
			p->callBack();
			cout << "[실행후] *th_func() reported *arg: " << p << " / " << arg << endl;
        }
 
//		pthread_t th = 0;
		static void *th_func(void *arg)
		{
			Parent *p = (Parent*)arg;
			cout << "*th_func() reported *arg: " << arg << endl;
			p->callBack();
		}
	public:
		Parent()
		{
            _beginthreadex(NULL, 0, td, this, 0, &id);
//			if(pthread_create(&th, NULL, th_func, (void*)this) < 0)
//				cerr << "thread not born." << endl;
//			else
//				cout << "thread has born." << endl;
		}
		~Parent()
		{
//			if(th!=0)
//				pthread_join(th, NULL);
//			cout << "joined. Parent leaving." << endl;
		}
};
 
class Child : public Parent
{
	public:
		void callBack()
		{
			cout << endl;
			cout << "child overrided." << endl;
		}
		Child() : Parent(){}
};
 
int main()
{
    //1. 클래스 오버라이드 - 사용 방법.
    //http://iopoi3.blog.me/130152756623
    Parent* p;
    Child ch;
	p = &ch;
	p->callBack(); //Parent에 virtual이 붙을경우 이런 방식으로 사용하면 안되네요. 
 
//2. 사용하신 방법 
//	Child *ch = new Child();
//	delete ch; 이 코드 때문에 죽습니다. 스레드 완료후 실행 되야 할거 같습니다. 
 
    system("PAUSE");
    return EXIT_SUCCESS;
}
 
1. 결과.
//Parent만  virtual 일 경우. 
[실행전] *th_func() reported *arg:
child overrided.
0x22ff40 / 0x22ff40
 
child overrided.
[실행후] *th_func() reported *arg: 0x22ff40 / 0x22ff40 
 
//둘다  virtual 일 경우. 
[실행전] *th_func() reported *arg:
child overrided.
0x22ff40 / 0x22ff40
 
child overrided.
[실행후] *th_func() reported *arg: 0x22ff40 / 0x22ff40
 
//Child만  virtual 일 경우. 
[실행전] *th_func() reported *arg: Original callBack() reported this: 0x22ff44 /
 0x22ff44
Original callBack() reported this: 0x22ff440x22ff44
 
[실행후] *th_func() reported *arg: 0x22ff44 / 0x22ff44
 
//둘다 virtual이 아닐경우. 
Original callBack() reported this: [실행전] *th_func() reported *arg: 0x22ff400x
22ff40
 / 0x22ff40
Original callBack() reported this: 0x22ff40
[실행후] *th_func() reported *arg: 0x22ff40 / 0x22ff40
 
 
2. 결과. 
Child만  virtual void callBack() 일 경우. 
[실행전] *th_func() reported *arg: 0x3d2484 / 0x3d2484
Original callBack() reported this: 0x3d2484
[실행후] *th_func() reported *arg: 0x3d2484 / 0x3d2484
 
둘다  virtual 일 경우. 
[실행전] *th_func() reported *arg: 0x3d2480 / 0x3d2480
child overrided.
[실행후] *th_func() reported *arg: 0x3d2480 / 0x3d2480
 
Parent만  virtual 일 경우. 
[실행전] *th_func() reported *arg: 0x3d2480 / 0x3d2480
child overrided.
[실행후] *th_func() reported *arg: 0x3d2480 / 0x3d2480
 
virtual이 없을 경우. 
[실행전] *th_func() reported *arg: 0x3d2480 / 0x3d2480
Original callBack() reported this: 0x3d2480
[실행후] *th_func() reported *arg: 0x3d2480 / 0x3d2480
 
 
 
#include <cstdlib>
#include <iostream>
 
#include <process.h>                 //_beginthreadex
 
using namespace std;
 
unsigned int __stdcall td(void* arg);
 
class UserSocket
{
public:
    void* pmain;
    void fn_setMain(void* p)
    {
        pmain = p;
    }
 
    virtual void onConnection_request(const int fd)
//    void onConnection_request(const int fd)
    {
        printf("UserSocket - onConnection_request(%d)\n", fd);
    }
 
    UserSocket(const char *xProto)
    {
        printf("UserSocket\n");
    }
    ~UserSocket()
    {
        printf("~UserSocket\n");
    }
 
private:
        unsigned int id;
 
private:
    static void *listen_thread_func(void *arg)
    {
    }
 
public:
    bool start_listen_thread()
    {
        printf("UserSocket - start_listen_thread PREV\n");
        _beginthreadex(NULL, 0, td, pmain, 0, &id);
        printf("UserSocket - start_listen_thread NEXT\n");
        return true;
    }
 
};
 
class CSocket : public UserSocket
{
public:
    void onConnection_request(const int fd)
    {
         printf("CSocket - onConnection_request(%d)\n", fd);
    }
public:
    CSocket() : UserSocket("tcp")
    {
         printf("CSocket\n");
    }
    ~CSocket()
    {
         printf("~CSocket\n");
    }
    void fn_run()
    {
         fn_setMain(this);
    }
};
 
 
unsigned int __stdcall td(void* arg)
{
    printf("UserSocket - td PREV\n");
    UserSocket* pus = (UserSocket*)arg;
    if(pus == NULL)
    {
        printf("pus is NULL\n");
        return 0;
    }
    printf("UserSocket - td CENTER\n");
    pus->onConnection_request(11);
    ((CSocket*)pus)->onConnection_request(12);
    printf("UserSocket - td NEXT\n");
}
 
 
int main(int argc, char *argv[])
{
    CSocket s;
    UserSocket* p = new UserSocket();
    p = &s;
    p->fn_setMain(&s);
    p->start_listen_thread();
 
    system("PAUSE");
    return EXIT_SUCCESS;
}
 
 
class UserSocket이 void onConnection_request(const int fd) 일 경우. 
UserSocket
CSocket
---------------------------------
UserSocket - start_listen_thread PREV
UserSocket - start_listen_thread NEXT
UserSocket - td PREV
UserSocket - td CENTER
UserSocket - onConnection_request(11)
CSocket - onConnection_request(12)
UserSocket - td NEXT
 
 
class UserSocket이 virtual void onConnection_request(const int fd) 일 경우. 
UserSocket
CSocket
UserSocket - start_listen_thread PREV
UserSocket - start_listen_thread NEXT
UserSocket - td PREV
UserSocket - td CENTER
CSocket - onConnection_request(11)
CSocket - onConnection_request(12)
UserSocket - td NEXT
*/

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.