사람들이 자주 C ++로 객체를 만드는 것을 보았습니다.
Thing myThing("asdf");
이 대신에 :
Thing myThing = Thing("asdf");
적어도 관련된 템플릿이없는 한 (gcc 사용) 작동하는 것 같습니다. 내 질문은 지금 첫 줄이 맞습니까? 그렇다면 사용해야합니까?
답변
두 줄은 사실 정확하지만 미묘하게 다른 일을합니다.
첫 번째 줄은 format의 생성자를 호출하여 스택에 새 객체를 만듭니다 Thing(const char*)
.
두 번째는 조금 더 복잡합니다. 본질적으로 다음을 수행합니다.
Thing
생성자를 사용하여 유형의 객체를 만듭니다.Thing(const char*)
Thing
생성자를 사용하여 유형의 객체를 만듭니다.Thing(const Thing&)
~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 부
특정 기준이 충족되면 객체 의 복사 생성자 및 / 또는 소멸자가 부작용이 있더라도 구현에서 클래스 객체의 복사 구성을 생략 할 수 있습니다 .
주석에서 컴파일러 플래그로 끄고 복사 동작을 확인하십시오.)