Programing

새로운 코드없이 C ++에서 생성자 호출

lottogame 2020. 7. 1. 08:02
반응형

새로운 코드없이 C ++에서 생성자 호출


사람들이 자주 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

Quite simply, both lines create the object on the stack, rather than on the heap as 'new' does. The second line actually involves a second call to a copy constructor, so it should be avoided (it also needs to be corrected as indicated in the comments). You should use the stack for small objects as much as possible since it is faster, however if your objects are going to survive for longer than the stack frame, then it's clearly the wrong choice.


Ideally, a compiler would optimize the second, but it's not required. The first is the best way. However, it's pretty critical to understand the distinction between stack and heap in C++, sine you must manage your own heap memory.


I played a bit with it and the syntax seems to get quite strange when a constructor takes no arguments. Let me give an example:

#include <iostream> 

using namespace std;

class Thing
{
public:
    Thing();
};

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

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

}

so just writing Thing myThing w/o brackets actually calls the constructor, while Thing myThing() makes the compiler thing you want to create a function pointer or something ??!!


In append to JaredPar answer

1-usual ctor, 2nd-function-like-ctor with temporary object.

Compile this source somewhere here http://melpon.org/wandbox/ with different compilers

// 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");
}

And you will see the result.

From ISO/IEC 14882 2003-10-15

8.5, part 12

Your 1st,2nd construction are called direct-initialization

12.1, part 13

A functional notation type conversion (5.2.3) can be used to create new objects of its type. [Note: The syntax looks like an explicit call of the constructor. ] ... An object created in this way is unnamed. [Note: 12.2 describes the lifetime of temporary objects. ] [Note: explicit constructor calls do not yield lvalues, see 3.10. ]


Where to read about RVO:

12 Special member functions / 12.8 Copying class objects/ Part 15

When certain criteria are met, an implementation is allowed to omit the copy construction of a class object, even if the copy constructor and/or destructor for the object have side effects.

Turn off it with compiler flag from comment to view such copy-behavior)

참고URL : https://stackoverflow.com/questions/2722879/calling-constructors-in-c-without-new

반응형