589

서브 클래스에서 슈퍼 클래스 생성자를 호출하기위한 C ++ 규칙은 무엇입니까?

예를 들어, Java에서는 하위 클래스 생성자의 첫 번째 줄로 사용해야한다는 것을 알고 있습니다. (그렇지 않으면 암묵적으로 수퍼 슈퍼 생성자가 호출됩니다. 누락 된 경우 컴파일 오류가 발생합니다) .


  • 그냥 질질 끌기 : "슈퍼 클래스"가 없습니다. 사실 C ++에서는 표준에서 전혀 언급하지 않습니다. 이 표현은 Java (아마도)에서 유래합니다. " 기본 클래스 " C ++에서. 내가 추측 컨대감독자단일 상위를 의미하며 C ++은 다중 상속을 허용합니다. - andreee

9 답변


801

기본 클래스 생성자는 인수가없는 경우 자동으로 호출됩니다. 인수를 사용하여 수퍼 클래스 생성자를 호출하려면 서브 클래스의 생성자 초기화 목록을 사용해야합니다. Java와 달리 C ++은 다중 상속 (더 좋거나 나쁨)을 지원하므로 기본 클래스는 "super ()"가 아닌 이름으로 참조되어야합니다.

class SuperClass
{
    public:

        SuperClass(int foo)
        {
            // do something with foo
        }
};

class SubClass : public SuperClass
{
    public:

        SubClass(int foo, int bar)
        : SuperClass(foo)    // Call the superclass constructor in the subclass' initialization list.
        {
            // do something with bar
        }
};

생성자의 초기화 목록에 대한 추가 정보이리이리.


  • ' 명시 적 '을 삭제했습니다. SuperClass 생성자에서. 단일 인수 생성자에 대한 모범 사례 임에도 불구하고 현재 진행중인 논의와 밀접한 관련이 없었습니다. 명시 적 키워드에 대한 자세한 내용은 다음을 참조하십시오.weblogs.asp.net/kennykerr/archive/2004/08/31/… - luke
  • 콜론 : 연산자 당신이 자식 클래스 생성자를 인스턴스화하기 전에 수퍼 클래스 생성자를 호출하는 데 사용되는,이 또한 메서드에 대한 사실이라고 생각해? - ha9u63ar
  • @hagubear, 생성자에만 유효, AFAIK - luke
  • 예를 들어, SubClass 객체를 인스턴스화 할 때,SubClass anObject(1,2), 않는다1그 다음에 전달된다.SuperClass(foo)(매개 변수의 인수가 됨foo)? 나는 문서를 높게, 낮게 검색하고 있지만, SubClass 생성자에 대한 인수를 SuperClass 생성자에 인수로 전달할 수 있다고 명시하지는 않습니다. - LazerSharks
  • @ 그네, 알아 차려라.: SuperClass(foo)일부.foo명시 적으로 수퍼 클래스의 생성자에 전달됩니다. - luke

206

C ++에서 생성자를 입력하기 전에 모든 수퍼 클래스 및 멤버 변수에 대한 인수가없는 생성자가 호출됩니다. 인수를 전달하려면 "생성자 연결"이라는 별도의 구문이 있으며 다음과 같습니다.

class Sub : public Base
{
  Sub(int x, int y)
  : Base(x), member(y)
  {
  }
  Type member;
};

이 시점에서 아무 것도 실행하지 않으면 이전에 완료된 기초 / 멤버가 소멸자를 호출하고 예외가 호출자에게 다시 전달됩니다. 연쇄 중에 예외를 잡으려면 함수 try 블록을 사용해야합니다.

class Sub : public Base
{
  Sub(int x, int y)
  try : Base(x), member(y)
  {
    // function body goes here
  } catch(const ExceptionType &e) {
    throw kaboom();
  }
  Type member;
};

이 양식에서는 try 블록~이다.함수 몸체가 아니라 함수 몸체. 이것은 암시 적 또는 명시 적 멤버 및 기본 클래스 초기화뿐만 아니라 함수 본문 중에 던진 예외를 잡을 수 있습니다. 그러나 함수 catch 블록이 다른 예외를 throw하지 않으면 런타임에서 원래 오류를 다시 발생시킵니다. 초기화 중 예외~ 할 수 없다.무시해라.


  • 두 번째 예제의 구문을 잘 모르겠습니다 ... try / catch 구조체가 생성자 본문을 대체합니까? - levik
  • 예. 섹션을 수정하고 실수를 수정했습니다 (try 키워드는 초기화 목록보다 먼저 사용됩니다). 나는 메모리에서 쓰는 대신 그것을 찾아야 만했다. 자주 사용되는 것은 아니다 :-) - puetzk
  • 이니셜 라이저에 대한 try / catch 구문을 포함 해 주셔서 감사합니다. C ++을 10 년 동안 사용해 본 적이 있습니다. 처음 본 적이 있습니다. - jakar
  • 나는 C ++을 오랫동안 사용해 왔음을 인정해야한다. 그리고 그것은 생성자 목록에서 try / catcn을 본 최초이다. - Cameron

45

C ++에는 생성자의 초기화 목록 개념이 있습니다.이 목록은 기본 클래스의 생성자를 호출 할 수 있고 호출해야 할 곳이며 데이터 멤버를 초기화해야하는 곳입니다. 초기화 목록은 콜론 다음에 그리고 생성자 본문 앞에 생성자 시그니처 다음에옵니다. 클래스 A가 있다고 가정 해 봅시다.


class A : public B
{
public:
  A(int a, int b, int c);
private:
  int b_, c_;
};

그런 다음 B가 int를 취하는 생성자를 가지고 있다고 가정하면 A의 생성자는 다음과 같이 보일 수 있습니다.


A::A(int a, int b, int c) 
  : B(a), b_(b), c_(c) // initialization list
{
  // do something
}

보시다시피 기본 클래스의 생성자는 초기화 목록에서 호출됩니다. 그런데 초기화 목록의 데이터 멤버를 초기화하는 것은 생성자 본문의 내부에 b_ 및 c_에 대한 값을 할당하는 것보다 바람직합니다. 이는 할당의 추가 비용을 저장하기 때문입니다.

데이터 멤버는 초기화 목록의 순서에 관계없이 클래스 정의에서 선언 된 순서대로 항상 초기화된다는 점에 유의하십시오. 데이터 멤버가 서로 의존하는 경우 발생할 수있는 이상한 버그를 방지하려면 초기화 목록 및 클래스 정의에서 멤버 순서가 항상 동일한 지 확인해야합니다. 같은 이유로 기본 클래스 생성자는 초기화 목록의 첫 번째 항목이어야합니다. 모두 생략하면 기본 클래스의 기본 생성자가 자동으로 호출됩니다. 이 경우 기본 클래스에 기본 생성자가 없으면 컴파일러 오류가 발생합니다.


  • 잠깐 ... 이니셜 라이저가 할당 비용을 줄인다 고합니다. 그러나 동일한 임무는 소집되면 그들 내부에서 일어나지 않습니까? - levik
  • 아니. 초기화와 할당은 다른 것들입니다. 생성자가 호출되면 기본값으로 간주되는 모든 데이터 멤버를 초기화하려고 시도합니다. 초기화 목록에서 기본값을 제공합니다. 따라서 두 경우 모두 초기화 비용이 발생합니다. - Dima
  • 그리고 몸 안의 과제를 사용하면 초기화 비용이 발생하고 그 위에 할당 비용이 부과됩니다. - Dima
  • 이 답변은 헤더 및 소스 파일이 있고 헤더에 초기화 목록을 원하지 않는 구문 변형을 보여주기 때문에 유용했습니다. 고맙습니다. - Benjamin
  • 너무 나쁜 나는 이것을 위해 단 하나의 upvote를 줄 수있다 !!! 우수 답변 - Rajesh

18

누구나 초기화 목록을 통해 생성자 호출을 언급했지만 부모 클래스의 생성자가 파생 된 멤버의 생성자 본문에서 명시 적으로 호출 될 수 있다고는 아무도 언급하지 않았습니다. 질문보기하위 클래스에서 기본 클래스의 생성자를 호출합니다. ' 생성자 본문예를 들면. 요점은 파생 클래스의 본문에서 부모 클래스 또는 수퍼 클래스 생성자에 대한 명시 적 호출을 사용하는 경우 실제로는 부모 클래스의 인스턴스를 만드는 것이며 파생 된 객체에서 부모 클래스 생성자를 호출하지 않는다는 것입니다 . 파생 클래스의 객체에서 상위 클래스 또는 상위 클래스 생성자를 호출하는 유일한 방법은 파생 클래스 생성자 본문이 아닌 초기화 목록을 사용하는 것입니다. 아마 "수퍼 클래스 생성자 호출"이라고해서는 안됩니다. 누군가가 혼란 스러울 수 있기 때문에 나는이 대답을 여기에 넣었다.


  • 이 답변은 몇 번 읽었을 때 다소 혼란 스럽지만 질문과 관련된 내용을 살펴 보았습니다. 나는 이것이 파생 클래스의 본문에서 부모 클래스 나 수퍼 클래스 생성자에 대한 명시 적 호출을 사용하는 경우 사실상 부모 클래스의 인스턴스를 만들고 부모 클래스를 호출하지 않는다고 말하는 것입니다 파생 된 객체의 생성자. 파생 클래스에서 상위 클래스 또는 상위 클래스 생성자를 호출하는 유일한 방법은 ' 개체가 파생 클래스 생성자 본문이 아닌 초기화 목록을 통해 있습니다. - Richard Chambers
  • @ 리차드 챔버스 영어가 제 1 언어가 아니기 때문에 혼란 스러울 지 모르지만 제가 말씀 드린 것을 정확하게 묘사했습니다. - TT_

17

상위 생성자에 값을 전달하는 유일한 방법은 초기화 목록을 사용하는 것입니다. 초기화 목록은 a :와 그 클래스 생성자에 전달할 값 및 목록을 사용하여 구현됩니다.

Class2::Class2(string id) : Class1(id) {
....
}

또한 부모 클래스에서 매개 변수를 사용하지 않는 생성자가있는 경우 하위 생성자를 실행하기 전에 자동으로 호출됩니다.


17

인수가없는 생성자가 있으면 파생 클래스 생성자가 실행되기 전에 호출됩니다.

인자를 가진 기본 생성자를 호출하려면 다음과 같이 파생 생성자에 명시 적으로 작성해야합니다.

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

C ++에서는 부모 생성자를 호출하지 않고 파생 클래스를 생성 할 수 없습니다. 그것은 arg가 아닌 C'tor 인 경우 자동으로 발생합니다. 위에 나온 것처럼 직접 파생 생성자를 호출하거나 코드가 컴파일되지 않으면 발생합니다.


7

CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }


9

기본 생성자에 기본 매개 변수가 있으면 기본 클래스가 자동으로 호출됩니다.

using namespace std;

class Base
{
    public:
    Base(int a=1) : _a(a) {}

    protected:
    int _a;
};

class Derived : public Base
{
  public:
  Derived() {}

  void printit() { cout << _a << endl; }
};

int main()
{
   Derived d;
   d.printit();
   return 0;
}

출력 : 1


  • 이는 특정 선언이 암시 적으로 만들어지기 때문입니다.Base(), 같은 몸을 가진Base(int)목표 플러스 목표에 대한 암시 적 초기화: _a{1}. 그것의 'Base()특정 기본 생성자가 init-list에 연결되어 있지 않으면 항상 호출됩니다. 그리고 다른 곳에서 언급했듯이, C ++ 11의 생성자와 중괄호 또는 동등한 초기화는 위임자가 이미 많은 경우에 코드 - 냄새를 풍기고있을 때 기본 인수를 덜 필요하게 만듭니다. - underscore_d

4

클래스가 여러 클래스에서 파생 될 때 생성자 호출 시퀀스는 언급되지 않았습니다. 시퀀스는 클래스를 파생시키는 동안 언급 된 것과 같습니다.


  • 아무도 그것에 대해 말하지 않았다면, 그것은 어디에 언급 되었습니까? - user207421
  • @EJP 질문은 규칙 호출에 관한 것이므로 답변의 호출 순서를 언급 할 가치가 있습니다. - darth_coder

연결된 질문


관련된 질문

최근 질문