Конструкторы производного класса
Общий класс:
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 шт):
С чего вы взяли, что нельзя? Проверьте:
CountDn c;
cout << (--c).get_count();
Для создания объекта с, создается объект Counter конструктором по умолчанию(первым вызывается этот конструктор), так как не указано как будет конструироваться базовый объект. А значит поле count для объекта с будет иметь нулевое значение. Так как он имеет тип unsigned, то его декремент не будет иметь определенного результата, поскольку никакое отрицательное число не может быть закодировано по типу unsigned int. А значит (--c) просто будет не уместным действием(какое значение передается в конструктор Counter, что вы хотите вернуть?...), пока вы не увеличили значение count каким то образом.
Простой перевод от Страуструпа :
Унаследованные конструкторы
Люди иногда путаются в том, что обычные правила области применимы к членам класса. В частности, член базового класса не находится в той же области, что и член производного класса:
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 равно нулю
}