ProgramingTip

C ++에서 다른 생성자 (생성자 체인)에서 생성 할 위험이없는 할 수 있습니까?

bestdevel 2020. 9. 28. 09:36
반응형

C ++에서 다른 생성자 (생성자 체인)에서 생성 할 위험이없는 할 수 있습니까?


A와 C #을 개발자 나 생성 해 통해 실행하는 데 사용 해요 :

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

C ++ 에서이 작업을 수행하는 방법이 있습니까?

클래스 이름을 호출하고 'this'키워드를 누르지 만 둘 다 실패했습니다.


C ++ 11 : 예!

C ++ 11 이상에는 이와 동일한 기능 ( 위임 생성자 라고 함 )이 있습니다.

구문은 C #과 약간의 약간의 수치입니다.

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};

C ++ 03 : 아니요

불행히도 C ++ 03에서는 수행 할 수있는 방법이 없지만 시뮬레이션하는 방법에는 두 가지가 있습니다.

  1. 기본 매개 변수를 통해 두 개 이상의 생성 가능성을 결합 할 수 있습니다.

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. init 메소드를 사용하여 공통 코드를 공유하십시오.

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

참조를 위해 C ++ FAQ 항목 을 참조하십시오.


아니요, C ++ 03에서 다른 생성자 (위임 생성자라고 함)를 호출 할 수 없습니다.

이것은 C ++ 11 (일명 C ++ 0x)에서 변경되어 다음 구문에 대한 지원을 추가했습니다.
( 위키 백과 에서 비교 예제 )

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};

호출 할 수 생성자에서 생성 호출 할 수 있습니다. 실행되고 실행됩니다. 최근에 누군가가 수행하는 것을 실행하고 Windows와 Linux에서 실행했습니다.

그것은 당신이 원하는 것을 만들지 않습니다. 내부 생성자는 외부 생성자가 생성합니다. 그들은 또한 다른 생성자이거나 재귀 호출을 호출합니다.

참고 : https://isocpp.org/wiki/faq/ctors#init-methods


에서 부모 클래스 생성자의 생성자를 호출 있다는 점을 지적 할 가치가 있습니다 . 예 :

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};

그러나 아니요, 동일한 클래스의 다른 생성 호출 할 수 없습니다.


에서 C ++ (11) 하는 생성자가 다른 생성자 over-로드를 호출 할 수 있습니다 :

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};

또한 이와 같이 멤버를 초기화 할 수도 있습니다.

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};

이렇게하면 초기화 도우미 메서드를 만들 필요가 없습니다. 초기화되지 않은 멤버를 사용하지면 생성자 또는 소멸자에서 가상 함수를 호출하지 않는 것이 좋습니다.


악한 사람이 포함 된 내부 "new"연산을 사용할 수 있습니다.

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};

나를 위해 일하는 것입니다.

편집하다

@ElvedinHamzagic이 지적했듯이 Foo에 메모리를 할당 한 개체가 있고 있으면 해제 할 수 없습니다. 이것은 상황을 복잡하게 만듭니다.

더 일반적인 예 :

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};

확실히 조금 덜 우아해 우네요. @JohnIdol의 솔루션이 훨씬 낫습니다.


아니요, C ++에서는 생성자에서 생성 호출 할 수 없습니다. 워렌이 지적했듯이 할 수있는 일은 다음과 같습니다.

  • 다른 서명을 사용하여 생성자 오버로드
  • 인수에 사용을 사용하여 "간단한"버전을 사용할 수 있습니다.

첫 번째 경우에는 한 생성 가능성이 다른 생성자에서 호출하여 코드를 사용할 수 없습니다. 물론 모든 초기화를 처리하는 별도의 개인 / 보호 된 메소드를 생성합니다.


Visual C ++에서는 생성자 내에서이 표기법을 사용할 수도 있습니다 : this-> Classname :: Classname (다른 생성자의 매개 변수). 아래 예를 참조하십시오.

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};

다른 곳에서 작동하는지 여부는 모르겠지만 Visual C ++ 2003 및 2008에서 테스트했습니다 . Java 및 C # 에서처럼 여러 생성 효과 방식으로 호출 할 수도 있습니다 .

추신 : 솔직히, 이것이 이전에 언급되지 않은 것에 놀랐습니다.


간단히 말해 C ++ 11 이전에는 할 수 없습니다.

C ++ 11에는 위임 생성자가 도입되었습니다 .

위임 생성자

클래스 자체의 이름이 멤버 이니셜 라이저 목록에서 클래스 또는 식별자로 나타나면 목록은 해당 멤버 이니셜 라이저로만 구성되어야합니다. 이러한 생성자를 위임 생성자라고하며 이니셜 라이저 목록의 유일한 멤버가 선택한 생성자는 대상 생성자입니다.

이 경우 대상 생성자는 오버로드 확인에 의해 선택되고 먼저 실행 된 다음 컨트롤이 위임 생성자로 반환되고 본문이 실행됩니다.

위임 생성자는 재귀적일 수 없습니다.

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};

위임 생성자는 전부 또는 전무 제안입니다. 생성자가 다른 생성자에게 위임하는 경우 호출 생성자는 초기화 목록에 다른 멤버를 포함 할 수 없습니다. 이것은 const / reference 멤버를 한 번만 초기화하는 것에 대해 생각한다면 의미가 있습니다.


아직 표시되지 않은 또 다른 옵션은 원하는 효과를 얻기 위해 원래 클래스 주위에 경량 인터페이스 클래스를 래핑하여 클래스를 두 개로 분할하는 것입니다.

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};

"next level up"대응 물을 호출해야하는 생성자가 많으면 복잡해질 수 있지만 소수의 생성자에 대해서는 실행 가능해야합니다.


private friend생성자의 응용 프로그램 논리를 구현하고 다양한 생성자가 호출 하는 메서드 의 사용을 제안합니다 . 다음은 그 예입니다.

StreamArrayReader개인 필드 가있는 클래스가 있다고 가정 합니다.

private:
    istream * in;
      // More private fields

그리고 두 개의 생성자를 정의하려고합니다.

public:
    StreamArrayReader(istream * in_stream);
    StreamArrayReader(char * filepath);
    // More constructors...

두 번째 것은 단순히 첫 번째 것을 사용하는 곳입니다 (물론 우리는 전자의 구현을 복제하고 싶지 않습니다). 이상적으로는 다음과 같은 작업을하고 싶습니다.

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    StreamArrayReader(&instream);
    instream.close();
}

그러나 이것은 C ++에서 허용되지 않습니다. 따라서 첫 번째 생성자가해야 할 일을 구현하는 private friend 메서드를 다음과 같이 정의 할 수 있습니다.

private:
  friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

이제이 메서드는 (친구이기 때문에)의 개인 필드에 액세스 할 수 있습니다 o. 그런 다음 첫 번째 생성자는 다음과 같습니다.

StreamArrayReader::StreamArrayReader(istream * is) {
    init_stream_array_reader(this, is);
}

새로 생성 된 사본에 대해 여러 사본이 생성되지는 않습니다. 두 번째는 다음과 같습니다.

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    init_stream_array_reader(this, &instream);
    instream.close();
}

즉, 한 생성자가 다른 생성자를 호출하는 대신 둘 다 개인 친구를 호출합니다!


이 접근 방식은 일부 클래스에 대해 작동 할 수 있습니다 (할당 연산자가 '잘'동작하는 경우).

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}

질문을 올바르게 이해하면 C ++에서 여러 생성자를 호출 할 수 있는지 묻는 것입니까?

그것이 당신이 찾고있는 것이라면, 그것은 불가능합니다.

각각 고유 한 인수 서명이있는 여러 생성자를 가질 수 있으며 새 개체를 인스턴스화 할 때 원하는 생성자를 호출 할 수 있습니다.

끝에 기본 인수가있는 하나의 생성자를 가질 수도 있습니다.

그러나 여러 생성자가 없을 수 있으며 각각을 개별적으로 호출 할 수 있습니다.


생성자를 호출 할 때 실제로 스택 또는 힙에서 메모리를 할당합니다. 따라서 다른 생성자에서 생성자를 호출하면 로컬 복사본이 생성됩니다. 그래서 우리는 우리가 초점을 맞추고있는 것이 아니라 다른 객체를 수정하고 있습니다.


결정하는 것보다 테스트하기가 더 쉬울 것입니다. :) 이것을 시도하십시오 :

#include <iostream>

class A {
public:
    A( int a) : m_a(a) {
        std::cout << "A::Ctor" << std::endl;    
    }
    ~A() {
        std::cout << "A::dtor" << std::endl;    
    }
public:
    int m_a;
};

class B : public A {
public:
    B( int a, int b) : m_b(b), A(a) {}
public:
    int m_b;
};

int main() {
    B b(9, 6);
    std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;    
    return 0;
}

98 std로 컴파일하십시오. g ++ main.cpp -std = c ++ 98 -o test_1

당신은 볼 것이다 :

A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor

그래서 :)

참고 URL : https://stackoverflow.com/questions/308276/can-i-call-a-constructor-from-another-constructor-do-constructor-chaining-in-c

반응형