C++ constructor calling and object creation

£可爱£侵袭症+ 提交于 2019-12-22 08:37:23

问题


class Test{
    public :
        int x;  
        Test()
        {
            x = 0;
            cout<<"constructor with no arguments called"<<endl;
        }
        Test(int xx)
        {
            x = xx;
            cout<<"constructor with single int argument called"<<endl;
        }

};


int main()
{
    Test a(10);
    Test aa = 10;
}

output: Program compiles and outputs

constructor with single int argument called

constructor with single int argument called

But now

class Test{
    public :
        int x;  
        Test()
        {
            x = 0;
            cout<<"constructor with no arguments called"<<endl;
        }
        Test(int xx)
        {
            x = xx;
            cout<<"constructor with single int argument called"<<endl;
        }

        Test( Test& xx)
        {
            x = xx.x;
            cout<<"copy constructor called"<<endl;
        }


};


int main()
{
    Test a(10);
    Test aa = 10;
}

compilation fails.

constructorinvokings.cc:36:7: error: no viable constructor copying variable of type 'Test'
        Test aa = 10;
             ^    ~~
constructorinvokings.cc:23:3: note: candidate constructor not viable: no known conversion from 'Test' to 'Test &' for 1st
      argument
                Test( Test& xx)
                ^
1 error generated.

I am new to C++.

Aren't Test a(10) and Test aa = 10; identical ?

why is the addition of copy constructor conflicting with Test aa=10?

if I modify Test(Test& xx) to Test(const Test& xx) it is working. But why is the compiler checking for copy constructor signature when we are trying to call constructor with integer argument.

Please clarify

Thanks in advance.


回答1:


All of the answers you've gotten so far are overly complicated and/or slightly misleading.

In the first construction/initialization:

T a(10);

The very obvious thing happens. The second construction/initialization is more interesting:

T aa = 10;

This is equivalent to:

T aa(T(10));

Meaning that you create a temporary object of type T, and then construct aa as a copy of this temporary. This means that the copy constructor is called.

C++ has a default copy constructor that it creates for a class when you have none explicitly declared. So even though the first version of class T has no copy constructor declared, there still is one. The one the compiler declares has this signature T(const T &).

Now in your second case where you declare something that looks like a copy constructor, you make the argument a T &, not a const T &. This means that the compiler, when trying to compile the second expression, tries to use your copy constructor, and it can't. It's complaining that the copy constructor you declared requires a non-const argument, and the argument it's being given is const. So it fails.

The other rule is that after the compiler has converted your initialization to T aa(T(10)); it is then allowed to transform it to T aa(10);. This is called 'copy elision'. The compiler is allowed, in certain circumstances, to skip calls to the copy constructor. But it may only do this after it verifies that the expression is correctly formed and doesn't generate any compiler errors when the copy constructor call is still there. So this is an optimization step that may affect exactly which parts of a program run, but cannot affect which programs are valid and which ones are in error (at least from the standpoint of whether or not they compile).




回答2:


Test a(10) and Test aa = 10; are not identical. The first one constructs a Test out of 10, while the second one constructs a Test out of 10 and then copy-constructs aa out of it. The addition of the copy constructor conflicts because it states that making a copy-construction is a mutable operation for both two operands. There are cases where copy-constructors take the source as a non const reference, but are somewhat complicated cases and is not your case here.

Edit: Note that in the second case the compiler is allowed to elide that copy-construction, and usually does.




回答3:


(Less verbose testcase here.)

The two forms of initialisation are actually not completely equivalent at all.

The former is your bog-standard construction:

T o(10);

The latter, however, is more complicated.

T o = 10;

Here, the expression on the RHS (10) is converted to type T, then the object o is copy-constructed from that converted expression. The copy construction is not possible here because your T(T&) prohibits the synthesis of an implicit T(T const&), and the temporary object on the RHS cannot be bound to a ref-to-non-const.

Now, the compiler is allowed to elide that copy-construction and this usually occurs, but the conversion must still be valid or the program is ill-formed.


[n3290: 8.5/13]: The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type; see below. [..]

[n3290: 8.5/14]: The initialization that occurs in the form

T x = a;

as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). —end note ]

[n3290: 8.5/16]: The semantics of initializers are as follows. [..] If the destination type is a (possibly cv-qualified) class type: [..] Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. [..]

[n3290: 12.8/31]: When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. [..] This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies): [..]

  • when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move. [..]

(I can't actually find a citation stating explicitly that the copy constructor must still be accessible even when the copy is elided, but this is the case.)




回答4:


I don't agree with K-ballo. When you define a c'tor with single argument, it can be [mis]used by compiler for implicit conversion. Which is what happens in this case.

So when you run the program it calls "c'tor with single arg" for construction of a AWA aa.

Test aa = 10;

There is no "copying" or "assignment" involved in this statement. Given that you've provided the compiler with a single arg c'tor, it'd [mis]use it in this case.

// So basically compiler converts this:
Test aa = 10;
// to
Test aa(10);

You can stop misuse by marking that c'tor as explicit. Just change Test( int x ) to explicit Test( int x ) See some explanation of explicit keyword here: What does the explicit keyword mean in C++?

if I modify Test(Test& xx) to Test(const Test& xx) it is working. But why is the compiler checking for copy constructor signature when we are trying to call constructor with integer argument.

I also didn't understand this and would like to know. :)

I observed that copy c'tor simply is never called.

#include<iostream>
using namespace std;

class Test
{
public :
    int x;
    Test() { x=0; cout << "    Test() - " << x << endl;}
    Test(int xx) { x=xx; cout << "    Test(int xx) - " << x << endl;}
    Test(Test& xx){ x=xx.x; cout << "    Test(Test& xx) - " << x << endl;}
    Test(const Test& xx){ x=xx.x; cout << "    Test(const Test& xx) - " << x << endl;}
    Test& operator= (const Test& xx) { x=xx.x; cout << "    Test& operator= (const Test& xx) - " << x << endl; return *this;}
};


int main()
{
    cout << "--Test a(10);--" << endl;
    Test a(10);
    cout << "--Test aa = 20;--" << endl;
    Test aa = 20;
    cout << "--Test aaa = aa;--" << endl;
    Test aaa = aa;
    cout << "--aaa = aa;--" << endl;
    aaa = aa;
    cout << "--aaa = 30;--" << endl;
    aaa = 30;
}
/*
OUTPUT:
    --Test a(10);--
        Test(int xx) - 10
    --Test aa = 20;--
        Test(int xx) - 20
    --Test aaa = aa;--
        Test(Test& xx) - 20
    --aaa = aa;--
        Test& operator= (const Test& xx) - 20
    --aaa = 30;--
        Test(int xx) - 30
        Test& operator= (const Test& xx) - 30
*/

and when copy c'tor with const arg Test(const Test& xx) is commented out:

We get compilation errors.

D:\Workspaces\CodeBlocks\Test\main.cpp: In function 'int main()':
D:\Workspaces\CodeBlocks\Test\main.cpp:21:19: error: no matching function for call to 'Test::Test(Test)'
D:\Workspaces\CodeBlocks\Test\main.cpp:10:9: note: candidates are: Test::Test(Test&)
D:\Workspaces\CodeBlocks\Test\main.cpp:9:9: note:                 Test::Test(int)
D:\Workspaces\CodeBlocks\Test\main.cpp:8:9: note:                 Test::Test()
Process terminated with status 1 (0 minutes, 0 seconds)
4 errors, 0 warnings

Line 21 is Test aa = 20;



来源:https://stackoverflow.com/questions/7457370/c-constructor-calling-and-object-creation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!