[완료] Super class 생성자에서 호출 되는 함수....

liveeasily의 이미지

안녕하세요,

C++ 로 작업중인데, 흔히 일어날 수 있을 것 같은 구조인데 답이 나오질 않네요

class Super
{
public:
  Super() {
     init();
  }
 
  void init() {
      ....
  }
};
 
class Sub : public Super
{
public:
  Sub() {
     ....
  }
 
  void init() {
     ....
  }
};

위 와 같은 형태로
Super Class 와 Sub Class 가 있습니다.

제가 원하는 것은
* Sub instance 가 생성될 때에는 Sub 의 void init() 함수만 호출 되는 것입니다.

하지만 위와 같은 형태에서는
* Super 의 void init() 이 먼저 호출되고 있습니다.
* Super 생성자 -> Super 의 void init() -> Sub 생성자 -> Sub 의 void init() 의 순서대로 호출 되고 있습니다.

어찌하면 Super instance 를 만들 때는 super 의 init() 만 호출되고,
Sub instance 를 만들 때는 sub 의 init() 만 호출될 수 있을까요?

liveeasily의 이미지

init() 함수를 virtual 로 선언하여도 Super class 생성자에서 호출 되는 init() 함수는 Super class 의 init() 이군요.. ㅜㅜ

liveeasily의 이미지

뭔가 맞지 않는 다는 생각이 드네요..

흠.. 결국 init() 을 생성자에서 호출 하지 않고,

Super 나 Sub instance 를 만든 곳에서 따로 호출을 해야 해결이 될 것 같네요..

다른 방법이 있을까요?

* 혼자 글 올리고 댓글달고 있군요 >.<

hayarobi의 이미지

자바랑은 틀리군요. 근데 보통은 C++방식으로 처리하는 것이 더 안전한 코드를 생성하는데 도움이 될 겁니다. 잘못 사용하면 초기화되지도 않은 변수값을 사용할 수도 있거든요.

제가 아는 언어 안에서는 Objective-C는 alloc 과 init이 별도로 존재하고 init이 형식 자체는 일반 메써드라서 자연스럽게(?) 오버라이딩이 가능하고요, JAVA는 컨스터럭터 안에서 호출되는 메써드도 다 오버라이드 됩니다.

굳이 하실거면 별도의 초기화 메써드를 구현한 다음에 팩터리 메써드 패턴을 이용해 가져오는 게 어떨까 싶네요.

=================
잠못자는 한솔아빠

=================
잠못자는 한솔아빠

liveeasily의 이미지

JAVA 좀 하다가 Python 좀 하다가 C 좀 하다가 C++ 하려니... 머리가.. 뒤죽박죽입니다 ㅜㅜ

netionics의 이미지

init을 가상으로 만들고 생성자에서 호출하지 말고 외부에서 직접 호출하도록 해야 합니다. 그 외에는 방법이 없습니다. 애초에 상속을 받는게 잘못된 방법같습니다.

:)

liveeasily의 이미지

init 함수를 virtual 로 선언하고 외부에서 호출 하는 형태로 바꾸었습니다.

말씀하신대로 지금 상속을 받을 지 말지 고민하고 있습니다

klara의 이미지

상속받은 클래스에 맞는 init을 생성자안에서 불러야 한다면, 생성자 그 자체가 init이겠지요.
생성자안에 init에서 할 것을 직접 적으면됩니다.

liveeasily의 이미지

Sub class 의 instance 생성 시,

Super class 의 생성자가 호출되기 때문에,

생성자 안에 넣어버린 Super class 의 init() 내용이 실행될 수 밖에 없지 않나요?

klara의 이미지

당연합니다.
그러니까 앞에서 적었듯이, init을 버리고, 직접 생성자에 적으시라고 한 것입니다.
init이 구체적으로 무엇을 하는 함수인진 모르겠지만, 상위 클래스의 초기화는 상위클래스가 하고, 하위 클래스의 초기화는 하위 클래스가 해야지요.

hayarobi의 이미지

방금 생각난 뻘짓인데요, 이렇게 하려는 목적이 부모클래스의 초기화를 건너뛰려는 목적이겠죠? 자식 클래스에서 다른 방식으로 초기화를 하려고요. 그게 아니면 그냥 부모클래스 초기화는 다 하고 자식 클래스에서 추가 초기화를 생성자에서 해 버리면 되니까요.

첫번째 방법은 RTTI를 이용해서 해당 인스턴스의 클래스 이름을 확인해서 super클래스나 그 부모클래스가 아니면 init을 건너뛰게 만드는 겁니다. 근데 C++의 동작을 저도 잘 몰라서 제대로 될지는 모르겠네요. 그리고 이러면 자식클래스는 init을 무조건 다 구현해야할테고요. 기타 몇 가지 부작용과 구조 문제는 일단 넘어가고요.

다른 방법은 부모클래스에 초기화를 직접 할 지 여부를 가지는 인자가 포함된 protected 생성자를 만드는 것이죠.

어느 방법이나 뻘스럽기 때문에 별로 이쁘지는 않네요.

그리고 자바 쪽에서도 생성자 안에서 호출하는 메써드가 오버라이드하는 것은 권장하지 않는 방법일 겁니다.

=================
잠못자는 한솔아빠

=================
잠못자는 한솔아빠

winner의 이미지

Super class의 생성자를 두 개를 만들거나 default 매개변수를 사용하여 subclass에서 생성하는지를 확인하는 방법입니다.

class Super
{
public:
  Super(bool callBySub = false) {
    if (false == callBySub)
    {
      init();
    }
  }
 
  void init() {
      ....
  }
};
 
class Sub : public Super
{
public:
  Sub() : Super(true) // 초기화목록을 이용하는 방법이 중요.
  {
     ....
  }
 
  void init() {
     ....
  }
};

Java에서 생성자 첫문장으로 super를 쓸 수 있는 것처럼 C++에서는 초기화 목록(Initializer list)를 활용한
super class의 호출할 생성자 선택이 가능합니다.

위 class들을 사용하는 client programmer가 Super(true)와 같은 형태를 생성하지 않기 위해서는
class 내 type을 만드는 또다른 기법이 필요하겠습니다.

하지만 OOP 기법들이 논의가 많이 되지 않고, C++가 세상 최고의 programming 언어로
OOP를 주도하던 시절이었으면 이런 방식이 참 바람직한(?) 방식이었겠으나
상속보다는 연관을 보다 강조하는 지금에 와서는 되도록 피해야 할 기법일 것 같습니다.

설령 Java라도 이것은 구현 상속과 관련된 사안이고, C++ 에서 이게 문제가 되는 것은 구현상속으로 인해 발생하는 문제를
우회하기 위한 방법(해결이 아닌...)이 Java 보다 까다롭기 때문일 뿐 현대 OOP 관점에서 설계에 이미 문제가 있다는 것은 틀리지 않기 때문입니다.
특히 이 방법은 super class에서 subclass 를 신경쓰고 있다는 점에서 이미 문제(불편함)이 있습니다.
상속을 하면서 subclass를 신경쓰는 기법은 정말 의도적으로 이루어지는게 아니라면 대부분 문제가 있습니다.
그런 의도로 만들어지는게 대게 framework이나, Design Pattern에서 template method 같은 기법이겠지요.
그런 경우는 통상 super class를 아예 생성못하고, 그야말로 상속을 받아야만 활용되는 형태가 보통입니다.

물론 상황에 따라서 처음 작성하신 기법이 필요할 수도 있을 것입니다.
언제나 항상 잘못된 것은 흔치 않으니까요.

써놓고 보니 이미 hayarobi님이 쓰셨네요.... ^_^.
네, protected가 이런 경우 적절하겠군요.

RTTI는 가상함수 호출과 마찬가지로 문제가 있습니다. 실제로 동작한다 하더라도 사용하지 마세요.
RTTI는 가상함수가 있는 경우만 사용할 수 있는 기법입니다. 가상함수가 없는 객체는 쓸 수 없죠.
특히나 Java와 달리 가상함수를 가지는 최상위 class Object가 없는 C++ 는 더욱 큰 문제입니다.

cynicjj의 이미지

흔히 일어날 수 있을것 같다고 하셨는데, 제가 볼땐 아닌것 같군요.
사실 상속관계가 아니라 형제 관계 아닌가요? (아니면 아예 상속관계가 없거나)
새로운 인터페이스(퓨어 버추얼)을 하나 만드시고,
두 클래스에서 그 인터페이스를 구현하는걸 검토해 보세요.

bugiii의 이미지

가상함수가 아닌데 같은 이름을 상속 받은 곳에서 사용하면 문법적으로 문제는 없지만 나중에 골치 아픈 문제가 생길 수 있기 때문에 사용하지 않는 것이 좋겠습니다.

Sub가 Super의 역할을 완전하게 하면서 추가적인 행동을 할 수 있고, 그 Super를 공통적으로 사용해야 한다면 다음과 같이 상속 구조를 바꿔서 한번 적용해보시기 바랍니다. (노출되는 인터페이스를 일반 멤버함수로 만들고 그 실질적인 동작을 강제하는 부분을 순수 가상함수로 만드는 것이 더 낫다고 생각합니다만 예제이므로...)

현재는 다음과 같은 상속입니다.

    Super
      |
      |
     Sub

자연 현상을 (생물의 계통도 등) 따라서 꼭 클래스 상속에 적용할 필요가 없다는 것은 많은 텍스트에서 언급하고 있는 사항입니다. 다음과 같은 형태로 상속을 하고 최상위 SuperInterface 에서 init 을 순수 가상함수로 만들어서 init 을 상속받은 쪽에서 강제로 구현하게 만들면 어떨까 합니다. 상속 받은 생성자에서 init을 호출하도록 부모가 강제할 수 있으면 좋겠지만, 부모의 생성자에서 아직 생성되지 않은 자식의 가상함수를 호출할 수는 없으므로, 이것까지 어떻게 할 수 있는 방법은 없겠습니다. (호출할 수는 있지만 부모와 자식의 상태가 완전하지 않으므로 문제가 발생할 소지가 많습니다.)

   SuperInterface
         |
         |
    +----+----+
    |         |
    |         |
  Super      Sub

소스

#include <iostream>
#include <typeinfo>
 
using namespace std;
 
#define print_func { cout << __PRETTY_FUNCTION__ << endl; }
 
class SuperInterface
{
public:
        SuperInterface() { print_func; }
        virtual void init() = 0;
};
 
class Super : public SuperInterface
{
public:
        Super() { print_func; init(); }
        virtual void init() { print_func; }
};
 
class Sub : public SuperInterface
{
public:
        Sub() { print_func; init(); }
        virtual void init() { print_func; }
};
 
void useSuper( const SuperInterface& super )
{
        print_func;
        cout << typeid(super).name() << endl;
}
 
int main()
{
        Super super;
        Sub sub;
 
        useSuper( super );
        useSuper( sub );
 
        return 0;
}

컴파일

# g++ --version
g++ (Debian 4.3.2-1.1) 4.3.2
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
# g++ test.cpp -o test

결과

# ./test
SuperInterface::SuperInterface()
Super::Super()
virtual void Super::init()
SuperInterface::SuperInterface()
Sub::Sub()
virtual void Sub::init()
void useSuper(const SuperInterface&)
5Super
void useSuper(const SuperInterface&)
3Sub

p.s 예제 하나 만드는 사이에 똑같은 내용의 댓글이 달렸군요.

댓글 달기

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