#c #c 98
#c #наследование #gcc #конструктор
Вопрос:
Почему этот код:
class A
{
public:
explicit A(int x) {}
};
class B: public A
{
};
int main(void)
{
B *b = new B(5);
delete b;
}
Приводит к этим ошибкам:
main.cpp : В функции ‘int main()’: main.cpp:13: ошибка: нет соответствующей функции для вызова ‘B::B (int)’ main.cpp:8: примечание: кандидатами являются: B::B() main.cpp:8: примечание: B::B(const Bamp;)
Разве B не должен наследовать конструктор A?
(для этого используется gcc)
Ответ №1:
Если ваш компилятор поддерживает стандарт C 11, существует наследование конструктора с использованием using
(каламбур). Для получения дополнительной информации смотрите статью C 11 в Википедии,,. Вы пишете:
class A
{
public:
explicit A(int x) {}
};
class B: public A
{
using A::A;
};
Это все или ничего — вы не можете наследовать только некоторые конструкторы, если вы напишете это, вы унаследуете их все. Чтобы наследовать только выбранные, вам нужно написать отдельные конструкторы вручную и вызвать базовый конструктор по мере необходимости из них.
Исторически конструкторы не могли наследоваться в стандарте C 03. Вам нужно было наследовать их вручную один за другим, вызывая базовую реализацию самостоятельно.
Для шаблонных базовых классов обратитесь к этому примеру:
using std::vector;
template<class T>
class my_vector : public vector<T> {
public:
using vector<T>::vector; ///Takes all vector's constructors
/* */
};
Комментарии:
1. Это довольно зло, потому что уже более года не было компилятора, который действительно мог бы создать приведенный выше код 🙂
2.@Mikhail: И clang, и g теперь должны поддерживать наследование конструкторов:clang.llvm.org/cxx_status.html gcc.gnu.org/projects/cxx0x.html Рекомендую проголосовать за это как за правильный ответ.
3. Я человек из будущего! MSVC 2015 заявляет о поддержке.
4. Можете ли вы использовать этот синтаксис только для наследования определенного конструктора?
5. @whoKnows: Нет, это все или ничего: en.cppreference.com/w/cpp/language/using_declaration
Ответ №2:
Конструкторы не наследуются. Они вызываются неявно или явно дочерним конструктором.
Компилятор создает конструктор по умолчанию (без аргументов) и конструктор копирования по умолчанию (с аргументом, который является ссылкой на тот же тип). Но если вам нужен конструктор, который будет принимать значение int, вы должны определить его явно.
class A
{
public:
explicit A(int x) {}
};
class B: public A
{
public:
explicit B(int x) : A(x) { }
};
ОБНОВЛЕНИЕ: в C 11 конструкторы могут наследоваться. Подробности см. в ответе Suma.
Ответ №3:
Это прямо со страницы Бьярне Страуструпа:
Если вы того пожелаете, вы все равно можете выстрелить себе в ногу, унаследовав конструкторы в производном классе, в котором вы определяете новые переменные-члены, нуждающиеся в инициализации:
struct B1 {
B1(int) { }
};
struct D1 : B1 {
using B1::B1; // implicitly declares D1(int)
int x;
};
void test()
{
D1 d(6); // Oops: d.x is not initialized
D1 e; // error: D1 has no default constructor
}
обратите внимание, что использование другой замечательной функции C 11 (инициализация члена):
int x = 77;
вместо
int x;
решило бы проблему
Комментарии:
1. В общем, я бы не рассматривал наследование ctors базового класса как стрельбу себе в ногу , но хорошая практика — в тех случаях, когда это возможно. Случаи, когда это возможно, — это те, где подкласс не добавляет элементы, которые требуют конструирования. Однако, если кто-то изменяет подкласс позже и добавляет элементы, которые требуют инициализации в конструкторе, нужно также изменить конструктор — и заменить наследование пользовательским конструктором. Если кто-то забывает об этом при добавлении членов подкласса, он попадает в foot, как будто не обновляет пользовательский ctor.
Ответ №4:
Вы должны явно определить конструктор в B и явно вызвать конструктор для родительского элемента.
B(int x) : A(x) { }
или
B() : A(5) { }
Комментарии:
1. Нет — начиная с C 11, вам больше не нужно.
Ответ №5:
Как насчет использования шаблонной функции для привязки всех конструкторов?
template <class... T> Derived(T... t) : Base(t...) {}
Комментарии:
1. Вероятно, вам следует сделать это с идеальной пересылкой: template < имя_типа… Аргументы > B ( аргументы amp;amp; … аргументы) : A( std::forward< Аргументы >(аргументы) … ) {}
2. И вы только что сломали
Derived
конструктор копирования.3. Должен ли конструктор Base также быть шаблонным? Когда вы вызываете Base(t …), тогда Base должен быть шаблоном для любого значения t?
4. @Zebrafish: Нет, это не так. И даже если бы это было так, это не обязательно было бы шаблонизировано таким же образом. Это потому, что когда создается экземпляр производного ctor, он вызывает базовый ctor. Этот базовый вызов ctor подлежит обычному разрешению перегрузки с учетом (созданных) аргументов
t...
. Вот почему появляется предупреждение о копировании ctor: это будет слепо пытаться вызватьBase::Base(Derived constamp; src)
, что вполне может повредить базовую часть src.
Ответ №6:
Правильный код — это
class A
{
public:
explicit A(int x) {}
};
class B: public A
{
public:
B(int a):A(a){
}
};
main()
{
B *b = new B(5);
delete b;
}
Ошибка в том, что класс B / c не имеет конструктора параметров, и, во-вторых, он должен иметь инициализатор базового класса для вызова конструктора конструктора параметров базового класса
Ответ №7:
Вот как я заставляю производные классы «наследовать» все родительские конструкторы. Я считаю, что это самый простой способ, поскольку он просто передает все аргументы конструктору родительского класса.
class Derived : public Parent {
public:
template <typename... Args>
Derived(Argsamp;amp;... args) : Parent(std::forward<Args>(args)...)
{
}
};
Или, если вы хотели бы иметь хороший макрос:
#define PARENT_CONSTRUCTOR(DERIVED, PARENT)
template<typename... Args>
DERIVED(Argsamp;amp;... args) : PARENT(std::forward<Args>(args)...)
class Derived : public Parent
{
public:
PARENT_CONSTRUCTOR(Derived, Parent)
{
}
};
Комментарии:
1. Не делайте первое, и, черт возьми, не делайте макрос. Первая часть определяет конструктор копирования и перемещения.
Ответ №8:
производный класс наследует все элементы (поля и методы) базового класса, но производный класс не может наследовать конструктор базового класса, потому что конструкторы не являются членами класса. Вместо наследования конструкторов производным классом, это позволяет вызывать только конструктор базового класса
class A
{
public:
explicit A(int x) {}
};
class B: public A
{
B(int x):A(x);
};
int main(void)
{
B *b = new B(5);
delete b;
}