Используя конструкторы базового класса C ++? - PullRequest
49 голосов
/ 11 ноября 2011

При работе с шаблонами я столкнулся с необходимостью сделать конструкторы базовых классов доступными из унаследованных классов для создания объектов, чтобы уменьшить количество операций копирования / вставки.Я думал сделать это через ключевое слово using таким же образом, как и в случае с функциями, но это не сработало.

class A
{
public: 
    A(int val) {}
};

class B : public A
{
};

class C : public A
{
public:
    C(const string &val) {}
};

class D : public A
{
public:
    D(const string &val) {}
    using A::A;              // g++ error: A::A names constructor
};

void main()
{
    B b(10);                // Ok.   (A::A constructor is not overlapped)
    C c(10);                // error: no matching function to call to 'C::C(int)'
}

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

Или существует только одна альтернатива для объявления новых конструкторов и вызова базовых конструкторов из списка инициализаторов?

Ответы [ 5 ]

54 голосов
/ 20 апреля 2014

Да, начиная с C ++ 11:

struct B2 {
    B2(int = 13, int = 42);
};
struct D2 : B2 {
    using B2::B2;
// The set of inherited constructors is
// 1. B2(const B2&)
// 2. B2(B2&&)
// 3. B2(int = 13, int = 42)
// 4. B2(int = 13)
// 5. B2()

// D2 has the following constructors:
// 1. D2()
// 2. D2(const D2&)
// 3. D2(D2&&)
// 4. D2(int, int) <- inherited
// 5. D2(int) <- inherited
};

Для получения дополнительной информации см. http://en.cppreference.com/w/cpp/language/using_declaration

37 голосов
/ 11 ноября 2011

Предпочитают инициализацию:

class C : public A
{
public:
    C(const string &val) : A(anInt) {}
};

В C ++ 11 вы можете использовать наследующие конструкторы (синтаксис которых приведен в вашем примере D).

Обновление: Наследующие конструкторы доступны в GCC начиная с версии 4.8.


Если вы не находите инициализацию привлекательной (например, из-за количества возможностей в вашем конкретном случае), то вы можете предпочесть этот подход для некоторых конструкций TMP:

class A
{
public: 
    A() {}
    virtual ~A() {}
    void init(int) { std::cout << "A\n"; }
};

class B : public A
{
public:
    B() : A() {}
    void init(int) { std::cout << "B\n"; }
};

class C : public A
{
public:
    C() : A() {}
    void init(int) { std::cout << "C\n"; }
};

class D : public A
{
public:
    D() : A() {}
    using A::init;
    void init(const std::string& s) { std::cout << "D -> " << s << "\n"; }
};

int main()
{
    B b; b.init(10);
    C c; c.init(10);
    D d; d.init(10); d.init("a");

    return 0;
}
8 голосов
/ 11 ноября 2011

Нет, это не так, как это делается.Обычный способ инициализации базового класса находится в списке инициализации:

class A
{
public: 
    A(int val) {}
};

class B : public A
{
public:
  B( int v) : A( v )
  {
  }
};


void main()
{
    B b(10);
}
4 голосов
/ 11 ноября 2011

Вам нужно будет объявить конструкторы в каждом из производных классов, а затем вызвать конструктор базового класса из списка инициализаторов:

class D : public A
{
public:
    D(const string &val) : A(0) {}
    D( int val ) : A( val ) {}
};

D variable1( "Hello" );
D variable2( 10 );

C ++ 11 позволяет вам использовать синтаксис using A :: A, который вы используете при объявлении D, но функции C ++ 11 не поддерживаются сейчас всеми компиляторами, поэтому лучше придерживаться более старого C ++ методы, пока эта функция не будет реализована во всех компиляторах, с которыми будет использоваться ваш код.

1 голос
/ 11 ноября 2011

Вот хорошее обсуждение правил вызова конструктора суперкласса . Вы всегда хотите, чтобы конструктор базового класса вызывался до конструктора производного класса, чтобы правильно сформировать объект. Вот почему эта форма используется

  B( int v) : A( v )
  {
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...