Почему конструкторы копирования не "цепляются", как конструкторы или деструкторы по умолчанию?

Это может быть вопрос с очевидным ответом или дублированием. Если да, извините, я его удалю.

Почему конструкторы копирования не привязаны (например, по умолчанию ctors или dtors), поэтому перед вызовом конструктора экземпляра производного класса вызывается конструктор копирования базового класса? С конструкторами копирования и деструкторами они вызываются в цепочке от базового к производному и производного к базе соответственно. Почему это не так для конструкторов копирования? Например, этот код:

class Base {
public:
    Base() : basedata(rand()) { }

    Base(const Base& src) : basedata(src.basedata) {
        cout << "Base::Base(const Base&)" << endl;
    }

    void printdata() {
        cout << basedata << endl;
    }

private:
    int basedata;
};

class Derived : public Base {
public:
    Derived() { }

    Derived(const Derived& d) {
        cout << "Derived::Derived(const Derived&)" << endl;
    }
};


srand(time(0));


Derived d1;      // basedata is initialised to rand() thanks to Base::Base()

d1.printdata();  // prints the random number

Derived d2 = d1; // basedata is initialised to rand() again from Base::Base()
                 // Derived::Derived(const Derived&) is called but not
                 // Base::Base(const Base&)

d2.printdata();  // prints a different random number

Конструктор копирования не делает (не может) действительно сделать копию объекта, потому что Derived::Derived(const Derived&) не может получить доступ к basedata, чтобы изменить его.

Есть ли что-то фундаментальное, что мне не хватает в конструкторах копий, так что моя ментальная модель неверна или есть какая-то тайная (или не тайная) причина для этого дизайна?

Ответ 1

Конструктор копирования не делает (не может) действительно сделать копию объекта, потому что Derived::Derived(const Derived&) не может получить доступ к pdata, чтобы изменить его.

Уверен, что он может:

Derived(const Derived& d)
    : Base(d)
{
    cout << "Derived::Derived(const B&)" << endl;
}

Если вы не укажете конструктор базового класса в списке инициализаторов, вызывается его конструктор по умолчанию. Если вы хотите, чтобы конструктор, отличный от конструктора по умолчанию, вызывался, вы должны указать, какой конструктор (и с каким аргументом) вы хотите вызвать.

Для чего это так: почему конструктор копирования не отличается от любого другого конструктора? В качестве примера практической проблемы:

struct Base
{
    Base() { }
    Base(Base volatile&) { } // (1)
    Base(Base const&)    { } // (2)
};

struct Derived : Base
{
    Derived(Derived&) { }
};

Какой из конструкторов Base copy вы ожидаете, что вызов конструктора Derived будет выполнен?

Ответ 2

Вы можете:

Derived(const Derived& d) : Base(d) {
    cout << "Derived::Derived(const B&)" << endl;
}

Это вызывает конструктор копирования Base в под-объекте Base d.

Ответ на вопрос "почему" я не знаю. Но обычно ответа нет. Комитету просто нужно было выбрать один вариант или другой. Это кажется более согласованным с остальной частью языка, где, например, Derived(int x) не будет автоматически вызывать Base(x).

Ответ 3

Это потому, что каждый конструктор вызывает по умолчанию базовый конструктор по умолчанию:

Derived(const Derived& d) {
    cout << "Derived::Derived(const B&)" << endl;
}

вызовет Base().

Это определяется стандартом. Я предпочитаю это так, а не вызываю конструктор копирования в классе. Вы можете, конечно, явно называть это.