Конструкторы производного класса

Общий класс:

class Counter {
protected:
    unsigned int count;
public:
    Counter(): count(0) {}
    Counter(int c): count(c) {}
    int get_count() { return count; }
    Counter operator++() { return Counter(++count); }
};

Потомок, версия 1:

class CountDn: public Counter
{
public:
    Counter operator--() { return Counter(--count); }
};

Потомок, версия 2:

class CountDn: public Counter
{
public:
    CountDn(): Counter() {}
    CountDn(int c): Counter(c) {}
    CountDn operator--() { return CountDn(--count); }
};

Скажите, пожалуйста. Почему в первой версии подкласса нельзя вернуть декремент класса CountDn? Всё равно ведь используется конструктор общего класса?

Я ссылаюсь на книгу Р.Лафоре "ООП в С++" - стр. 364, пункт "Определение производного класса": "... в то же время CountDn наследует все возможности класса Counter: конструктор и методы"

P.S. Может читаю устаревшую литературу?


Ответы (2 шт):

Автор решения: AR Hovsepyan

С чего вы взяли, что нельзя? Проверьте:

CountDn c;    
cout << (--c).get_count();   

Для создания объекта с, создается объект Counter конструктором по умолчанию(первым вызывается этот конструктор), так как не указано как будет конструироваться базовый объект. А значит поле count для объекта с будет иметь нулевое значение. Так как он имеет тип unsigned, то его декремент не будет иметь определенного результата, поскольку никакое отрицательное число не может быть закодировано по типу unsigned int. А значит (--c) просто будет не уместным действием(какое значение передается в конструктор Counter, что вы хотите вернуть?...), пока вы не увеличили значение count каким то образом.

→ Ссылка
Автор решения: AlexGlebe

Простой перевод от Страуструпа :

Унаследованные конструкторы

Люди иногда путаются в том, что обычные правила области применимы к членам класса. В частности, член базового класса не находится в той же области, что и член производного класса:

struct B {
    void f(double);
};

struct D : B {
    void f(int);
};

B b;   b.f(4.5);    // хорошо
D d;   d.f(4.5);    // сюрприз: вызывает f (int) с аргументом 4

В C++98, мы можем "поднять" набор перегруженных функций из базового класса в производный класс:

struct B {
    void f(double);
};

struct D : B {
    using B::f;     // приведите все f()-ки из B в область видимости
    void f(int);    // добавьте новую f()
};

B b;   b.f(4.5);    // хорошо
D d;   d.f(4.5);    // хорошо: вызывает D::f (double) , который является B::f(double)

Я уже говорил, что "Немногим больше, чем историческая случайность, мешает использовать это для работы как для конструктора, так и для обычной функции-члена." C++11 обеспечивает это средство:

class Derived : public Base { 
public: 
    using Base::f;    // поднять базовые f в область действия производного - работает в C++98
    void f(char);     // обеспечить новый f
    void f(int);      // этот f предпочтительнее Base::f(int) 

    using Base::Base; // поднять базовые конструкторы производной области -- только в C++11
    Derived(char);    // предоставить новый конструктор
    Derived(int);     // предпочтите этот конструктор против Base::Base(int) 
    // ...
}; 

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

struct B1 {
    B1(int) { }
};

struct D1 : B1 {
    using B1::B1; // неявно объявляет D1(int)
    int x;
};

void test()
{
    D1 d(6);    // Упс: d. x не инициализируется
    D1 e;       // ошибка: D1 не имеет конструктора по умолчанию
}

Вы можете удалить пулю из вашей ноги с помощью члена-инициализатора:

    struct D1 : B1 {
        using B1::B1;   // неявно объявляет D1(int)
        int x{0};   // Примечание: x инициализируется
    };

    void test()
    {
        D1 d(6);    // d. x равно нулю
    }
→ Ссылка