Calling constructors in c++ without new

前端 未结 7 1532
离开以前
离开以前 2020-12-04 05:20

I\'ve often seen that people create objects in C++ using

Thing myThing(\"asdf\");

Instead of this:

Thing myThing = Thing(\"         


        
相关标签:
7条回答
  • 2020-12-04 06:00

    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 ??!!

    0 讨论(0)
  • 2020-12-04 06:02

    Both lines are in fact correct but do subtly different things.

    The first line creates a new object on the stack by calling a constructor of the format Thing(const char*).

    The second one is a bit more complex. It essentially does the following

    1. Create an object of type Thing using the constructor Thing(const char*)
    2. Create an object of type Thing using the constructor Thing(const Thing&)
    3. Call ~Thing() on the object created in step #1
    0 讨论(0)
  • 2020-12-04 06:04

    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.

    0 讨论(0)
  • 2020-12-04 06:05

    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)

    0 讨论(0)
  • 2020-12-04 06:08

    I assume with the second line you actually mean:

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

    which would be the standard way of creating new dynamic objects (necessary for dynamic binding and polymorphism) and storing their address to a pointer. Your code does what JaredPar described, namely creating two objects (one passed a const char*, the other passed a const Thing&), and then calling the destructor (~Thing()) on the first object (the const char* one).

    By contrast, this:

    Thing thing("uiae");
    

    creates a static object which is destroyed automatically upon exiting the current scope.

    0 讨论(0)
  • 2020-12-04 06:08

    The compiler may well optimize the second form into the first form, but it doesn't have to.

    #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;
    }
    

    Output from 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
    
    0 讨论(0)
提交回复
热议问题