c++ quiz

June 2, 2012

Рассмотрим простую прогу с наследованием:

#include <stdio.h>
//
struct C {
    C() {
        printf ("- C::C()\n");
    }
};
struct D : public C {
    D(const C & o) : owner (o) {
        printf ("  `-D::D()\n");
    }
  private:
    const C & owner;
};
//
int main() {
    C c;
    //
    D d1(c);
    D d2(d1);
    //
    return 0;
}

Вопрос: что она выведет?

Чтобы окончательно вас сбить с толку рассмотрим объявленные и используемые в программе конструкторы для D:

struct D : public C {
    D(const C & o) {
    // ....
    }
    // ...
}
//
int main() {
    C c;
    //
    D d1(c);  // [1]
    D d2(d1); // [2]
    // ...
}

Итак, ответ:

$ g++ a.cc -o a && ./a
- C::C()
- C::C()
  `-D::D()
# а конструктора для d2 и нет! :]

Как так?

А вот как:

Конструктор копии по умолчанию в нашем случае выглядит примерно так:

C::C(const C & c)
{}
D::D(const D & d) : C(d), owner (d.owner)
{}

Часто это довольно гадкий и нежелательный эффект. В моём случае class С был низкоуровневым классом помощи посчета ссылок, для которого конструктор копии по умолчанию делал полную фигню и плодил утечки памяти.

Реальный код, в котором я вчера нашел этот эффект не предполагал создания копии и должен был быть написан следующим образом:

//
int main() {
    C c;
    //
    D d1(c);
    D d2(static_cast<const C &>(d1)); // как для 'd1'
    //
    return 0;
}

Чтобы впредь избежать проблем в дочерних классах, наследуемых от class С я просто спрятал конструкторы копии и присваивания:

#include <stdio.h>
//
struct C {
    C() {
        printf ("- C::C()\n");
    }
  private:
    C(const C &);              /* forbidden */
    C & operator= (const C &); /* forbidden */
};

В этом случае компилятор начнет ныть, что не может вызвать конструктор копии:

В функции «int main()»:
 замечание: synthesized method «D::D(const D&)» first required here

Добавление этого кода позволило найти еще пару таких-же проблемных мест.