2009-05-06 6 views
19

C++에서 명시 적으로 클래스의 생성자를 범위 확인 연산자, 즉 className::className()을 사용하여 호출 할 수 있습니다. 정확히 어디서 전화해야하는지 궁금 해서요.C++에서 명시 적으로 생성자를 호출하는 이유

+5

생성자를 직접 호출 할 수 있다고 말하는 것은 올바르지 않습니다. 표준에는 명시 적으로 (12.1/1)이 있습니다. "생성자에는 이름이 없습니다." 함수 스타일 캐스트 또는 새 배치와 같은 다른 구문을 통해서만 생성자를 호출 할 수 있습니다. –

답변

13

: 예를 들어

class BaseClass 
{ 
public: 
    BaseClass(const std::string& name) : m_name(name) { } 

    const std::string& getName() const { return m_name; } 

private: 

    const std::string m_name; 

//... 

}; 


class DerivedClass : public BaseClass 
{ 
public: 

    DerivedClass(const std::string& name) : BaseClass(name) { } 

// ... 
}; 

class TestClass : 
{ 
public: 
    TestClass(int testValue); //... 
}; 

class UniqueTestClass 
    : public BaseClass 
    , public TestClass 
{ 
public: 
    UniqueTestClass() 
     : BaseClass("UniqueTest") 
     , TestClass(42) 
    { } 

// ... 
}; 

....

그 외에는 유틸리티가 표시되지 않습니다. 너무 어려서 내가 실제로하고있는 것을 알지 못했을 때만 다른 코드에서 생성자를 호출했습니다 ...

+4

파생 된 클래스의 인스턴스가 생성 될 때 C++이 부모 클래스의 생성자를 암시 적으로 호출하지만 이니셜 라이저 목록에서 특정 부모 클래스 생성자를 명시 적으로 호출하지 않는 한 기본 생성자를 호출합니다. –

+0

예, 제 예제에서는 BaseClass의 유일한 유효한 생성자에 몇 가지 매개 변수가 필요하다는 것을 확인했습니다. 나는 명시 적으로 기본 생성자 호출을 요구하는 경우를 기억하지 못합니다. 어쩌면 가상 상속인가? – Klaim

0

당신이 일반적으로 생성자에 대해 사용하지 않을 것이라고 생각하지 않습니다. 그러나 다른 네임 스페이스에 두 개의 클래스가있는 경우 필요합니다. 예를 들어,이 두 가지 작성 클래스의 차이를 지정하려면 Xml::ElementChemistry::Element.

일반적으로 클래스 이름은 범위 분석 연산자와 함께 사용되어 상속 된 클래스의 부모에 대한 함수를 호출합니다. 따라서 Animal에서 상속받은 클래스 Dog가 있고이 두 클래스 모두 Eat() 함수를 다르게 정의하면 someDog라는 Dog 객체에서 Animal 버전의 eat을 사용하려는 경우가있을 수 있습니다. 내 C++ 구문은 약간 녹슬었지만이 경우에는 someDog.Animal::Eat()이라고 생각합니다. 대부분의 경우, 일부 매개 변수를 필요로 자식 클래스 생성자에서

42

때때로 임시 생성자를 명시 적으로 사용하여 생성자를 생성하는 경우가 있습니다. 당신은 생성자와 몇 가지 클래스가있는 경우 예를 들어, :

class Foo 
{ 
    Foo(char* c, int i); 
}; 

및 기능

void Bar(Foo foo); 

을하지만 주위 푸가 없습니다, 당신은

Bar(Foo("hello", 5)); 

이 작업을 수행 할 수 캐스트와 같습니다. 실제로 매개 변수 하나만 사용하는 생성자가있는 경우 C++ 컴파일러는 해당 생성자를 사용하여 암시 적 캐스트를 수행합니다.

이 아닌 이미 존재하는 개체에서 생성자를 호출하는 것은 합법입니다. 즉, 할 수 없습니다

Foo foo; 
foo.Foo(); // compile error! 

당신이 무엇을 하든지간에. 그러나 메모리를 할당하지 않고 생성자를 호출 할 수 있습니다. 즉, 새로 배치을위한 것입니다.

char buffer[sizeof(Foo)];  // a bit of memory 
Foo* foo = new(buffer) Foo(); // construct a Foo inside buffer 

새로운 메모리를 할당하면 새 메모리를 할당하는 대신 해당 위치에 개체가 생성됩니다. 이 사용은 악의적 인 것으로 간주되며 대부분의 코드 유형에서는 드물지만 임베디드 및 데이터 구조 코드에서는 일반적입니다.

예를 들어, std::vector::push_back은이 기술을 사용하여 복사 생성자를 호출합니다. 이렇게하면 빈 객체를 만들고 할당 연산자를 사용하는 대신 하나의 복사본 만 수행하면됩니다.

+4

+1을 새로 배치합니다. 이상한 일이지만, 자신이하는 일을 안다면 유용 할 수 있습니다. –

+0

"실제로 매개 변수 하나만 사용하는 생성자가있는 경우 C++ 컴파일러는 해당 생성자를 사용하여 암시 적 캐스트를 수행합니다." 이는 많은 사람들이 기본적으로 단일 arg 생성자에 명시 키워드를 넣고 매개 변수 유형에서 클래스 유형으로의 implict casting을 원한다면 제거하는 것입니다. –

+1

-1 생성자를 호출하지 않습니다. 구문은 ' (ctor-arg 목록)'이며' :: (ctor-arg 목록)'과 같지 않은 의미를 지니고 있습니다. 이 구문은 생성자를 호출하는 것처럼 보이게합니다. 사실, 함수처럼 생성자를 "호출"하지 않습니다. –

3

나는 컴파일러 오류 C2585에 대한 오류 메시지가 실제로 생성자 범위 해상도 연산자를 사용해야하는 이유 최고의 이유를 제공합니다 생각, 그리고 찰리의 대답으로 수행합니다

클래스에서 변환 또는 다중 상속에 기반한 구조 유형. 형식이 동일한 기본 클래스를 두 번 이상 상속하는 경우 변환 함수 또는 연산자는 범위 확인 (: :)을 사용하여 변환에서 사용할 상속 된 클래스를 지정해야합니다.

BaseClass가 있다고 가정하고 BaseClassA와 BaseClassB 모두 BaseClass를 상속 한 다음 DerivedClass가 BaseClassA와 BaseClassB를 상속한다고 가정합니다.

DerivedClass를 BaseClassA 또는 BaseClassB로 변환하기 위해 변환 또는 연산자 오버로드를 수행하는 경우 변환에 사용할 생성자 (복사 생성자, IIRC와 같은 것을 생각하고 있음)를 식별해야합니다.

2

일반적으로 생성자를 직접 호출하지 않습니다. new 연산자는 그것을 호출하거나 서브 클래스는 부모 클래스의 생성자를 호출합니다. C++에서 기본 클래스는 파생 클래스의 생성자가 시작되기 전에 완전히 구성되어야합니다.

생성자를 직접 호출하는 유일한 방법은 새 기능을 사용하지 않고 메모리를 관리하는 극히 드문 경우입니다. 그리고 그때조차도 그렇게해서는 안됩니다. 대신 연산자 new의 배치 양식을 사용해야합니다.

0

클래스 생성자를 노출하려는 유효한 유스 케이스가 있습니다. 예를 들어 아레나 할당자를 사용하여 자체 메모리 관리를 수행하려면 할당 및 객체 초기화로 구성된 2 단계 구성이 필요합니다.

내가 취하는 접근 방식은 다른 많은 언어와 비슷합니다. 나는 단지 잘 알려진 공용 메소드 (Construct(), init()과 같은 것)에 내 건설 코드를 넣고 필요할 때 직접 호출한다.

생성자와 일치하는 이러한 메서드의 오버로드를 만들 수 있습니다. 당신의 정규 생성자는 그것들을 호출합니다. 코드에 커다란 주석을 달아 다른 사람에게이 작업을하고 있다는 경고를 보내어 중요한 건설 코드를 잘못된 위치에 추가하지 않도록하십시오.

건설 중 과부하가 사용 된 경우에도 소멸자 메서드가 하나뿐이므로 소멸자를 초기화되지 않은 멤버에 대해 강력하게 만듭니다.

다시 초기화 할 수있는 초기화 프로그램을 작성하는 것을 권장하지 않습니다. 초기화되지 않은 메모리와 실제로 실제 데이터를 보유하기 때문에 가비지가있는 객체를보고있는 경우를 말하는 것이 어렵습니다.

가장 어려운 문제는 가상 메소드가있는 클래스에서 발생합니다. 이 경우 컴파일러는 일반적으로 클래스 시작 부분에 숨겨진 필드로 vtable 함수 테이블 포인터를 연결합니다. 이 포인터를 수동으로 초기화 할 수는 있지만 기본적으로 컴파일러의 특정 동작에 따라 달라 지므로 동료를 재미있게 볼 수 있습니다.

게재 위치 새 항목은 여러 측면에서 손상되었습니다. 배열의 구조/파괴에서 하나의 경우이므로 나는 그것을 사용하지 않는 경향이있다.

0

다음 프로그램을 고려하십시오.

template<class T> 
double GetAverage(T tArray[], int nElements) 
{ 
T tSum = T(); // tSum = 0 

for (int nIndex = 0; nIndex < nElements; ++nIndex) 
{ 
    tSum += tArray[nIndex]; 
} 

// Whatever type of T is, convert to double 
return double(tSum)/nElements; 
} 

이렇게하면 변수를 초기화하기 위해 명시 적으로 기본 생성자가 호출됩니다.

관련 문제