C ++에서 새로운 생성자없이 생성자 호출 템플릿이없는 한 (gcc 사용)

사람들이 자주 C ++로 객체를 만드는 것을 보았습니다.

Thing myThing("asdf");

이 대신에 :

Thing myThing = Thing("asdf");

적어도 관련된 템플릿이없는 한 (gcc 사용) 작동하는 것 같습니다. 내 질문은 지금 첫 줄이 맞습니까? 그렇다면 사용해야합니까?



답변

두 줄은 사실 정확하지만 미묘하게 다른 일을합니다.

첫 번째 줄은 format의 생성자를 호출하여 스택에 새 객체를 만듭니다 Thing(const char*).

두 번째는 조금 더 복잡합니다. 본질적으로 다음을 수행합니다.

  1. Thing생성자를 사용하여 유형의 객체를 만듭니다.Thing(const char*)
  2. Thing생성자를 사용하여 유형의 객체를 만듭니다.Thing(const Thing&)
  3. ~Thing()1 단계에서 생성 한 개체를 호출 합니다.

답변

나는 당신이 실제로 의미하는 두 번째 줄로 가정합니다 :

Thing *thing = new Thing("uiae");

이는 새로운 동적 객체 (동적 바인딩 및 다형성에 필요)를 만들고 주소를 포인터에 저장 하는 표준 방법입니다 . 귀하의 코드는 JaredPar가 설명 한대로, 즉 두 개의 객체 (하나는 a를 전달 const char*하고 다른 하나는 a 를 전달 const Thing&)를 생성 한 다음 ~Thing()첫 번째 객체 ( const char*하나) 에서 소멸자 ( ) 를 호출합니다 .

대조적으로, 이것은 :

Thing thing("uiae");

현재 범위를 종료하면 자동으로 제거되는 정적 객체를 만듭니다.


답변

컴파일러는 두 번째 양식을 첫 번째 양식으로 최적화 할 수 있지만 반드시 그럴 필요는 없습니다.

#include <iostream>

class A
{
    public:
        A() { std::cerr << "Empty constructor" << std::endl; }
        A(const A&) { std::cerr << "Copy constructor" << std::endl; }
        A(const char* str) { std::cerr << "char constructor: " << str << std::endl; }
        ~A() { std::cerr << "destructor" << std::endl; }
};

void direct()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void assignment()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a = A(__FUNCTION__);
    static_cast<void>(a); // avoid warnings about unused variables
}

void prove_copy_constructor_is_called()
{
    std::cerr << std::endl << "TEST: " << __FUNCTION__ << std::endl;
    A a(__FUNCTION__);
    A b = a;
    static_cast<void>(b); // avoid warnings about unused variables
}

int main()
{
    direct();
    assignment();
    prove_copy_constructor_is_called();
    return 0;
}

gcc 4.4의 출력 :

TEST: direct
char constructor: direct
destructor

TEST: assignment
char constructor: assignment
destructor

TEST: prove_copy_constructor_is_called
char constructor: prove_copy_constructor_is_called
Copy constructor
destructor
destructor


답변

간단히 말해서 두 줄 모두 ‘신규’처럼 힙이 아닌 스택에 객체를 만듭니다. 두 번째 줄에는 실제로 복사 생성자에 대한 두 번째 호출이 포함되므로 피해야합니다 (주석에 표시된대로 수정해야 함). 작은 개체에는 스택이 빠르기 때문에 가능한 한 많이 사용해야합니다. 그러나 개체가 스택 프레임보다 오래 생존 할 경우 분명히 잘못된 선택입니다.


답변

이상적으로는 컴파일러가 두 번째를 최적화하지만 필수는 아닙니다. 첫 번째 방법이 가장 좋습니다. 그러나 C ++에서 스택과 힙의 차이점을 이해하는 것은 매우 중요합니다.


답변

나는 그것을 조금 연주했고 생성자가 인수를 취하지 않으면 구문이 상당히 이상해 보입니다. 예를 들어 보겠습니다.

#include <iostream>

using namespace std;

class Thing
{
public:
    Thing();
};

Thing::Thing()
{
    cout << "Hi" << endl;
}

int main()
{
    //Thing myThing(); // Does not work
    Thing myThing; // Works

}

따라서 브라켓없이 Thing myThing을 작성하면 실제로 생성자가 호출되는 반면 Thing myThing ()은 컴파일러가 함수 포인터 또는 무언가를 만들고 싶은 것을 만듭니다.


답변

JaredPar 답변에 추가

임시 물체가있는 1 개의 특이한 ctor, 2 차 함수와 유사한 ctor

다른 컴파일러로 http://melpon.org/wandbox/ 어딘가에이 소스를 컴파일하십시오 .

// turn off rvo for clang, gcc with '-fno-elide-constructors'

#include <stdio.h>
class Thing {
public:
    Thing(const char*){puts(__FUNCTION__ );}
    Thing(const Thing&){puts(__FUNCTION__ );}
    ~Thing(){puts(__FUNCTION__);}
};
int main(int /*argc*/, const char** /*argv*/) {
    Thing myThing = Thing("asdf");
}

그리고 당신은 결과를 볼 수 있습니다.

ISO / IEC 14882 2003-10-15에서

8.5, 12 부

첫 번째, 두 번째 구성을 직접 초기화라고합니다.

12.1, 13 부

기능 표기법 유형 변환 (5.2.3)을 사용하여 해당 유형의 새 객체를 만들 수 있습니다. [참고 : 구문은 생성자를 명시 적으로 호출하는 것처럼 보입니다. ] … 이런 식으로 생성 된 객체의 이름은 없습니다. [참고 : 12.2는 임시 객체의 수명에 대해 설명합니다. ] [참고 : 명시 적 생성자 호출은 lvalue를 생성하지 않습니다. 3.10을 참조하십시오. ]


RVO에 대한 정보 :

12 특수 멤버 함수 / 12.8 클래스 객체 복사 / 15 부

특정 기준이 충족되면 객체 의 복사 생성자 및 / 또는 소멸자가 부작용이 있더라도 구현에서 클래스 객체의 복사 구성을 생략 할 수 있습니다 .

주석에서 컴파일러 플래그로 끄고 복사 동작을 확인하십시오.)


이 글은 C++ 카테고리에 분류되었고 태그가 있으며 님에 의해 에 작성되었습니다.