Copy constructor called everytime a default constructor is called?

I was going through some of the questions on constructor initialization list when I bumped into this.

Consider this:

class Student {
    public:
        Student() {
            id = 0;
        }
        Student(int i) {
            id = i;
        }
    private:
        int id;
};

Now, check this out:

By the time you get in the body of the constructor, all fields have already been constructed; if they have default constructors, those were already called. Now, if you assign a value to them in the body of the constructor, you are calling the copy constructor. That is inefficient, because two constructors end up being called instead of one.

Source: What does a colon following a C++ constructor name do?

So, does it mean that when I call the parameter-less constructor, the copy constructor is also being called?

Please explain. This is really confusing.

Particularly the meaning of the first line:

By the time you get in the body of the constructor, all fields have already been constructed


It means that int id has already been initialized before you get to the line id = 0 . Because no explicit initializer has been specified, it has been default initialized. Additionally, because it is an int , the initialization rules tell use that it will have some indeterminate value.

In practice, for an int , or any type that is extremely cheap to initialize, this doesn't matter too much.

If instead of using an int member, we used a class, we could more clearly see what is actually going on behind the scenes:

#include <iostream>

class Verbose {
    public:
        Verbose() {
            std::cout << __PRETTY_FUNCTION__ << "n";
        }

        Verbose(int) {
            std::cout << __PRETTY_FUNCTION__ << "n";
        }

        Verbose(Verbose const &) {
            std::cout << __PRETTY_FUNCTION__ << "n";
        }

        Verbose & operator=(Verbose const &) {
            std::cout << __PRETTY_FUNCTION__ << "n";
            return *this;
        }

        ~Verbose() {
            std::cout << __PRETTY_FUNCTION__ << "n";
        }
};

class Object {
    public:
        Verbose v;

        Object() {
            v = Verbose(3);
        }
};

int main() {
    Object o;
}

This code will output:

Verbose::Verbose()
Verbose::Verbose(int)
Verbose &Verbose::operator=(const Verbose &)
Verbose::~Verbose()
Verbose::~Verbose()

Note that we:

  • We use the default constructor to create v .
  • We create a temporary Verbose(3)
  • We then use the assignment operator to assign the temporary to the member variable.
  • We then destroy the temporary.
  • When Object o goes out of scope, we then destroy the member variable.
  • Note that we basically constructed Verbose v twice! We first used the default constructor, and then we basically rebuilt it using the operator= call. If we used an initializer list, we could reduce that down to one call to Verbose(int) .


    They mean this

    Student() {
        id = 0;
    }
    

    Is less efficient than

    Student() : id(0) {}
    

    In this particular example, id would be initialized in a single step since the member is just an int .

    In contrast, the latter method would make a difference if Student had more complex members, like perhaps Backpack and Books . If these classes weren't POD, the first method would take two steps to initialize the member, and the second would only take one step to initialize.

    链接地址: http://www.djcxy.com/p/73164.html

    上一篇: 定义的行为警告?

    下一篇: 每次调用一个默认构造函数时调用构造函数?