Passing same type object by reference?

Why is Constructor 2 invoked in CAT func(CAT someCat)? (After my knowledge) I am not passing any reference to CAT constructor within that function, so the normal CAT() constructor should be called instead.

Please help me understand this. If you could explain me step by step the entire process I would be very grateful to you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "iostream"

using namespace std;

class CAT
{
    public:

        // Constructor 1
        CAT() 
        {
            cout << "CAT\\CAT(): Constructor 1 called..\n";
        }

        // Constructor 2
        CAT(CAT &)
        {
            cout << "CAT\\CAT(CAT &): Constructor 2 called..\n"; 
        }

        ~CAT()
        {
            cout << "Destructor called..\n";
        }  
};

CAT func(CAT someCat)
{
    cout << "CAT func(CAT): Returning new CAT..\n";
    
    return someCat;
}

int main()
{
    CAT Frisky;
    
    func(Frisky);
    
    int tmp; cin >> tmp;
    
    return 0;    
}



OUTPUT:

1
2
3
4
5
6
CAT\CAT(): Constructor 1 called..
CAT\CAT(CAT &): Constructor 2 called..
CAT func(CAT): Returning new CAT..
CAT\CAT(CAT &): Constructor 2 called..
Destructor called..
Destructor called..
Last edited on
You're passing a CAT by value, so the copy constructor (CAT::CAT(CAT &) or CAT::CAT(const CAT &). In your case you've only defined the former) is called. When passing references to a function no constructor needs to be called because no new object is constructed.
constructor 2 is called because passing object by reference value on "func( frisky)"

add 'cout << "pause \n";'

1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
    CAT Frisky;

    cout << "pause \n" ;
    
    func(Frisky);
    
    int tmp; cin >> tmp;
    
    return 0;    
}




[output]
CAT\CAT(): Constructor 1 called..
pause
CAT\CAT(CAT &): Constructor 2 called..
CAT func(CAT): Returning new CAT..
CAT\CAT(CAT &): Constructor 2 called..
Destructor called..
Destructor called..
s
Destructor called..
/output]
Last edited on
You're passing a CAT by value, so the copy constructor [...] is called.

Why is it called?

constructor 2 is called because passing object by reference on "func( frisky)"

Where is it passed by reference? Please have a look at my following comments and correct my judgment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// New function 'func' is declared. The returned value is of type CAT.
// Function takes as parameter a variable of type CAT: someCat

CAT func(CAT someCat)
{
    cout << "CAT func(CAT): Returning new CAT..\n";
    
    return someCat; // Function returns someCat which is of type CAT
}

[.....]

    CAT Frisky; // Create 'Frisky' of type CAT
    
    func(Frisky); // Call 'func' with 'Frisky' as parameter
Last edited on
Last edited on
Where is it passed by reference? Please have a look at my following comments and correct my judgment:
There are no passes by reference in the call to func().

Why is [the copy constructor] called?
Copy constructors are called to, unsurprisingly, construct copies. That's what it means to pass by value. You're passing to the callee a copy of an object in the caller. That copy has to be constructed somehow. and that's what the copy constructor is for.
That copy has to be constructed somehow. and that's what the copy constructor is for.

Can't be used the original constructor to do that? Why do I have to declare another constructor with reference as parameter?

I am confused because I don't know how the 'copy' of the object is actually done.

1. The object 'Frisky' is instantiated.
2. The function 'func()' is invoked by 'main()'
3. Another object identical with 'Frisky' is created with temporary scope for 'func()'
4. 'func()' definition is called

At part 3: why do I need another constructor with CAT value passed by reference to create a replica of 'Frisky'. Can't the original 'CAT()' be used to do that? If no, why not?
Last edited on
Because what the function gets is a COPY of the object passed in. Not a default-constructed object. You said it yourself:

Another object identical with 'Frisky'

What sense would that make, if it didn't matter what the value of the object you passed in was, because all the function ever saw was a default-constructed object?
Last edited on
Can't be used the original constructor to do that?
Two questions:
1. Suppose you have this class:
1
2
3
4
5
6
7
class A{
    int a;
public:
    A(){
        this->a = /*???*/;
    }
};
If A::A() was to be used as a copy constructor, how would you initialize A::a so that it has the same value as a different instance of A?

2. It's possible for a class to not have a default constructor (the constructor that takes no parameters). For example:
1
2
3
4
5
6
7
class A{
    int a;
public:
    A(std::string *foo){
        this->a = /*???*/;
    }
};
What should the compiler pass to this constructor?

why do I need another constructor with CAT value passed by reference to create a replica of 'Frisky'.
You need someplace to get the data from which the copy will be made. In your case CAT has no data, so you could have simply defined no copy constructor (the compiler would have defined one for you), but what if the class did have data?
1
2
3
4
5
6
7
class A{
    int a;
public:
    A(const A &other){
        this->a = other.a;
    }
};
I understand now. Thank you very much for your time everyone, I really appreciate it.

I hope I did not give you headache :)
In some ways this is like the Miranda law. My paraphrasing:
You have the right to remain silent. Anything you say may used against you. You have the right to an attorney... if you cannot afford one , one will be provided for you


To continue my analogy,
- "silent": not needing to explicitly define constructor, destructor, copy constructor, assignment operator, move constructor, etc.
- anything you "say" may be used against you: if you do make one or more of the above, they will be prioritized
- if you cannot "afford": compiler will provide them for you automagically

Perhaps some more information about the objects you're creating and destroying will be helpful:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <string>

using namespace std;

class Cat
{
public:

    Cat(string name) : name_(name)
    {
        id_ = ++counter_;
        cout << "Normal constructor of \"" << name_ << "\" #" << 
                id_ << '\n';
        
    }

    // This is the normal copy constructor signature -- this way,
    //   with const ref, it's clear the parameter is read-only
    Cat(const Cat& c)
    {
        id_ = ++counter_;
        cout << "Copy constructor with parameter \"" << 
                c.name_ << "\" #" << id_ << '\n';
        name_ = c.name_;
    }

    Cat(Cat& c)
    {
        id_ = ++counter_;
        cout << "Non-const copy constructor with parameter \"" << 
                c.name_ << "\" #" << id_ << '\n';
        name_ = c.name_;
    }

    ~Cat()
    {
        cout << "Destroying \""<< name_ << "\" #" << id_ << '\n' << '\n';
    }

private:
    static int counter_;
    string name_;
    int id_;
};
int Cat::counter_ = 0;


// Function that makes a value copy
void Func(Cat cat)
{
}

int main()
{
    Cat frisky("frisky");      // #1
    Func(frisky);              // #2
    Func((const Cat&)frisky);  // #3
    
    return 0;    
}


Normal constructor of "frisky" #1
Non-const copy constructor with parameter "frisky" #2
Destroying "frisky" #2

Copy constructor with parameter "frisky" #3
Destroying "frisky" #3

Destroying "frisky" #1
Last edited on
Topic archived. No new replies allowed.