생성자가 상속되지 않는 이유는 무엇입니까?


33

생성자가 기본 클래스에서 상속 된 경우 문제가 무엇인지 혼동됩니다. Cpp Primer Plus는 말합니다.

생성자는 새로운 객체를 생성한다는 점에서 다른 클래스 메소드와 다릅니다. 반면에 다른 객체는 기존 객체에 의해 호출됩니다 . 이것이 생성자가 상속되지 않는 이유 중 하나 입니다. 상속은 파생 된 개체가 기본 클래스 메서드를 사용할 수 있지만 생성자의 경우 생성자가 작업을 완료 한 후에 개체가 존재하지 않음을 의미합니다.

객체 생성이 완료되기 전에 생성자가 호출된다는 것을 이해합니다.

자식 클래스가 상속하면 어떻게 문제를 일으킬 수 있습니까 ( 상속을 통해 자식 클래스가 부모 클래스 메서드를 재정의 할 수 있음을 의미합니다 .

객체를 만드는 동안을 제외하고는 코드 내에서 생성자를 명시 적으로 호출 할 필요가 없다는 것을 알고 있습니다. 그럼에도 불구하고 어떤 메커니즘을 사용하여 부모 구성자를 호출 할 수 있습니다 [cpp, 사용 ::또는 사용 member initialiser list, In Java 사용 super]. Java에서는 첫 번째 줄에서 그것을 호출해야합니다. 부모 객체가 먼저 생성 된 다음 하위 객체 구성이 진행되도록하는 것입니다.

그것을 무시할 수 있습니다. 그러나 이것이 문제가 될 수있는 상황을 생각해 낼 수는 없습니다. 자식이 부모 생성자를 상속하면 무엇이 잘못 될 수 있습니까?

이것은 불필요한 기능을 상속받지 않기위한 것입니다. 아니면 더 있습니까?


C ++ 11로 속도를 낼 수는 없지만 생성자 상속의 형태가 있다고 생각 합니다.
yannis

3
생성자에서 무엇을 하든지 소멸자를주의해야합니다.
Manoj R

2
"생성자 상속"의 의미를 정의해야한다고 생각합니다. 모든 답변은 "생성자 상속"의 의미에 대한 변형이있는 것 같습니다. 나는 그들 중 어느 것도 나의 초기 해석과 일치하지 않는다고 생각합니다. "상속"의 "회색 상자"에있는 설명은 "파생 개체가 기본 클래스 메서드를 사용할 수 있음을 의미합니다"는 생성자가 상속됨을 의미합니다. 파생 된 개체는 항상 기본 클래스 생성자 메서드 인 ALWAYS를 사용할 수 있고 사용합니다.
Dunk

1
생성자 상속에 대한 설명에 감사드립니다. 이제 답변을 얻을 수 있습니다.
덩크

답변:


41

파생 클래스의 생성자는 기본 클래스 생성자가 할 필요가없고 알지 못하는 추가 작업을 수행해야하므로 C ++에는 생성자를 올바르게 상속 할 수 없습니다. 이러한 추가 작업은 파생 클래스의 데이터 멤버를 초기화하는 것입니다 (일반적인 구현에서는 vclasser가 파생 클래스 vtable을 참조하도록 설정).

클래스가 생성되면 항상 발생해야하는 여러 가지가 있습니다. 기본 클래스 (있는 경우)의 생성자와 직접 멤버를 호출해야하며 가상 함수가있는 경우 vpointer를 올바르게 설정해야합니다. . 클래스에 생성자를 제공하지 않으면 컴파일러 필요한 작업을 수행하고 다른 작업은 수행 하지 않는 생성자를 만듭니다. 당신이 경우 어떻게 생성자를 제공하지만, 필요한 작업의 일부를 놓치지 (예를 들어, 일부 회원의 초기화)를 선택한 다음, 컴파일러는 자동으로 생성자에 누락 된 작업을 추가합니다. 이런 식으로 컴파일러는 각 클래스에 적어도 하나의 생성자가 있고 각 생성자가 생성 한 객체를 완전히 초기화하도록합니다.

C ++ 11에서는 기본 클래스의 생성자와 동일한 인수를 가져와 해당 인수를 기본 클래스.
공식적으로 상속이라고하지만 파생 클래스 별 함수가 여전히 있기 때문에 실제로는 그렇지 않습니다. 이제는 명시 적으로 작성하는 대신 컴파일러에서 생성합니다.

이 기능은 다음과 같이 작동합니다.

struct Base {
    Base(int a) : i(a) {}
    int i;
};

struct Derived : Base {
    Derived(int a, std::string s) : Base(a), m(s) {}

    using Base::Base; // Inherit Base's constructors.
    // Equivalent to:
    //Derived(int a) : Base(a), m() {}

    std::string m;
};

Derived이제 두 개의 생성자가 있습니다 (복사 / 이동 생성자를 계산하지 않음). 하나는 정수와 문자열을 취하고 다른 하나는 정수를 취합니다.


그래서 이것은 자식 클래스에 자체 생성자가 없을 것이라고 가정합니다. 상속 된 생성자는 분명히 자식 멤버와 초기화를 인식하지 못합니다
Suvarna Pattayil

C ++은 클래스 가 자체 생성자를 가지지 않는 방식으로 정의됩니다 . 실제로하는 '생성자 상속'을 고려하지 않는 이유입니다 상속. 지루한 상용구 작성을 피하는 방법 일뿐입니다.
Bart van Ingen Schenau

2
혼동은 빈 생성자조차도 무대 뒤에서 일한다는 사실에 기인한다고 생각합니다. 따라서 생성자는 실제로 내부 작업과 작성하는 두 부분으로 구성됩니다. 그것들은 잘 분리되어 있지 않기 때문에 생성자가 상속되지 않습니다.
Sarien

1
m()예를 들어, 어디에서 왔는지 , 그리고 유형이 어떻게 바뀌 었는지 표시하십시오 int.
중복 제거기

using Base::Base암묵적 으로 할 수 있습니까? 파생 클래스에서 해당 줄을 잊어 버렸고 모든 파생 클래스에서 생성자를 상속해야하는 경우 큰 결과를 초래할 수 있습니다
Post Self

7

"부모 생성자를 상속"한다는 것은 무슨 의미인지 명확하지 않습니다. 오버라이드 라는 단어를 사용하면 다형성 가상 함수처럼 동작 하는 생성자 에 대해 생각할 수 있습니다 . "가상 생성자"라는 용어는 의도적으로 사용하지 않습니다. 왜냐하면 다른 개체를 생성하기 위해 이미 존재하는 개체 인스턴스가 필요한 코드 패턴의 일반적인 이름 이기 때문 입니다 .

"가상 생성자"패턴 외부에서 다형성 생성자에 대한 유틸리티는 거의 없으며 실제 다형성 생성자가 사용될 수있는 구체적인 시나리오를 제시하기가 어렵습니다. 원격으로 유효한 C ++이 아닌 매우 고안된 예 :

struct Base {
  virtual Base(unsigned p1, unsigned p2) {...}
};

struct Derived: public Base {
  Derived(unsigned p1, unsigned p2) : Base(p1, p2) override {...}
};

int main(void) {
  unsigned p1 = 0;
  unsigned p2 = 42;
  Derived *p_d1 = new Base(p1, p2); // This might call Derived(unsigned, unsigned).
  Derived *p_d2 = nullptr;
  p_d2 = new Base(p1, p2); // This might call Derived(unsigned, unsigned) too.
}

이 경우 호출되는 생성자는 생성되거나 할당되는 변수의 구체적인 유형에 따라 다릅니다. 구문 분석 / 코드 생성 중에 감지하는 것은 복잡하며 유틸리티가 없습니다. 구체적으로 구체적인 유형을 알고 파생 클래스에 대한 특정 생성자를 작성했습니다. 다음과 같은 유효한 C ++ 코드는 정확히 동일하고 약간 짧으며 코드의 기능이 더 명확합니다.

struct Base {
  Base(unsigned p1, unsigned p2) {...}
};

struct Derived: public Base {
  Derived(unsigned p1, unsigned p2) : Base(p1, p2) {...}
};

int main(void) {
  unsigned p1 = 0;
  unsigned p2 = 42;
  Derived *p_d1 = new Derived(p1, p2); 
  Derived *p_d2 = nullptr;
  p_d2 = new Derived(p1, p2);
}


두 번째 해석 또는 추가 질문은 기본 클래스 생성자가 명시 적으로 숨겨지지 않는 한 모든 파생 클래스에 자동으로 존재하는 경우입니다.

자식이 부모 생성자를 상속하면 무엇이 잘못 될 수 있습니까?

파생 클래스를 구성하는 데 사용되지 않는 부모 생성자를 숨기려면 추가 코드를 작성해야합니다. 파생 클래스가 특정 매개 변수와 관련이없는 방식으로 기본 클래스를 특수화 할 때 발생할 수 있습니다.

일반적인 예는 사각형과 사각형입니다 (정사각형과 사각형은 일반적으로 Liskov로 대체 할 수 없으므로 디자인이 좋지 않지만 문제를 강조합니다).

struct Rectangle {
  Rectangle(unsigned width, unsigned height) {...}
};

struct Square : public Rectangle {
  explicit Square(unsigned side) : Rectangle(side, side) {...}
};

Square가 Rectangle의 두 값 생성자를 상속했다면 높이와 너비가 다른 정사각형을 만들 수 있습니다 ... 논리적으로 잘못되었으므로 해당 생성자를 숨기고 싶을 것입니다.


3

생성자가 상속되지 않는 이유 : 대답은 놀랍게도 간단합니다. 기본 클래스의 생성자는 기본 클래스를 "빌드"하고 상속 된 클래스의 생성자는 상속 된 클래스를 "빌드"합니다. 상속 된 클래스가 생성자를 상속하면 생성자는 기본 클래스 유형의 객체를 만들려고 시도하고 상속 된 클래스 유형의 객체를 "빌드"할 수 없습니다.

어떤 종류의 수업을 도입하려는 목적을 이길 수 있습니까?


3

파생 클래스가 기본 클래스 생성자를 재정의 할 수있게하는 가장 분명한 문제는 파생 클래스의 개발자가 이제 기본 클래스를 구성하는 방법을 알고 있다는 점입니다. 파생 클래스가 기본 클래스를 올바르게 구성하지 않으면 어떻게됩니까?

또한 기본 클래스가 다른 파생 형식과 올바르게 또는 호환되도록 구성되지 않았다는 보장이 없으므로 기본 클래스 개체 컬렉션이 서로 호환되는 것으로 더 이상 믿을 수 없으므로 Liskov-Substitution 원칙이 더 이상 적용되지 않습니다.

하나 이상의 상속 수준이 추가되면 더욱 복잡해집니다. 이제 파생 클래스는 모든 기본 클래스를 체인으로 구성하는 방법을 알아야합니다.

그런 다음 상속 계층 구조의 맨 위에 새 기본 클래스를 추가하면 어떻게됩니까? 파생 클래스 생성자를 모두 업데이트해야합니다.


2

생성자는 다른 방법과 근본적으로 다릅니다.

  1. 쓰지 않으면 생성됩니다.
  2. 모든 기본 클래스 생성자는 수동으로 수행하지 않아도 암시 적으로 호출됩니다.
  3. 명시 적으로 호출하지 않고 객체를 생성하여 호출합니다.

그렇다면 왜 상속받지 못합니까? 간단한 답변 : 항상 재정의가 있기 때문에 생성되거나 수동으로 작성됩니다.

모든 클래스에 생성자가 필요한 이유는 무엇입니까? 그것은 복잡한 질문이며 대답은 컴파일러에 따라 다릅니다. "사소한"생성자와 같은 것이 있는데, 컴파일러는 그것이 호출 될 것임을 강제하지 않습니다. 나는 이것이 상속에 의해 의미하는 바에 가장 가까운 것이라고 생각하지만 위에서 언급 한 세 가지 이유 때문에 생성자를 일반 메소드와 비교하는 것은 실제로 유용하지 않다고 생각합니다. :)


1

모든 클래스는 기본 클래스를 포함하여 생성자가 필요합니다.
특수 생성자를 만드는 경우를 제외하고 C ++은 기본 생성자를 만듭니다.
기본 클래스가 특수 생성자를 사용하는 경우 둘 다 동일하더라도 파생 클래스에 특수 생성자를 작성하고 다시 연결해야합니다.
C ++ 11을 사용하면 다음을 사용 하여 생성자에서 코드 중복을 피할 수 있습니다 .

Class A : public B {
using B:B;
....
  1. 상속 생성자 집합은

    • 기본 클래스의 모든 비 템플릿 생성자 (있는 경우 생략 매개 변수를 생략 한 후) (C ++ 14부터)
    • 기본 인수 또는 생략 부호 매개 변수가있는 각 생성자에 대해 생략 부호를 삭제하고 인수의 끝에서 기본 인수를 하나씩 생략하여 구성되는 모든 생성자 서명
    • 기본 클래스의 모든 생성자 템플릿 (있는 경우 생략 매개 변수를 생략 한 후) (C ++ 14부터)
    • 기본 인수 또는 말줄임표가있는 각 생성자 템플릿에 대해 줄임표를 삭제하고 인수 끝에서 기본 인수를 하나씩 생략하여 생성 된 모든 생성자 서명
  2. 기본 생성자 또는 복사 / 이동 생성자가 아니며 파생 클래스의 사용자 정의 생성자와 일치하지 않는 상속 된 모든 생성자는 파생 클래스에서 암시 적으로 선언됩니다. 기본 매개 변수는 상속되지 않습니다


0

당신이 사용할 수있는:

MyClass() : Base()

이렇게 해야하는지 묻고 있습니까?

하위 클래스에는 생성자에서 초기화해야 할 추가 속성이 있거나 기본 클래스 변수를 다른 방식으로 초기화 할 수 있습니다.

하위 유형 객체를 다른 방법으로 작성 하시겠습니까?


감사. 실제로 다른 생성자 클래스와 마찬가지로 생성자가 자식 클래스에서 상속되지 않는 이유를 알아 내려고 노력 중입니다.
수완 나 Pattayil
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.