Как я могу подделать наследование конструктора в C ++ 03? - PullRequest
1 голос
/ 29 октября 2011

Насколько я знаю, вы не можете наследовать конструкторы в C ++.Но существуют ситуации, когда может потребоваться, чтобы вы выглядели так, как будто вы можете создавать экземпляры унаследованных классов так же, как вы создаете их базу:

struct Base {
  int i;
  int const j;
  Base(int i, int j) : i(i), j(j) {}
};

// option 1)
struct Derived1 : Base {
  Derived1(int i, int j) : Base(i,j) {}
};
Base* baseFactory() {
  return new Derived1(42,47);
}

// option 2)
struct Derived2 : Base {
};
Base* baseFactory() {
  Base* b = new Derived2();
  b->i = 42;
  b->j = 47; // broken!
  // alternative could be to implement Base::operator=
  return b;
}

Обратите внимание, что производные классы могут быть созданы по умолчанию, если они недля их базового класса (классов).

Вариант 1 - это то, что обычно делается, я думаю, но вы печатаете код, не выражая ничего нового.Вариант 2 нарушает const правильность (и не позволяет использовать ссылки в качестве членов), потому что все должно быть назначаемым.

Есть ли способ обойти это?

Редактировать : C ++ 11 великолепен, но, к сожалению, мне нужно решение C ++ 03.

Ответы [ 4 ]

3 голосов
/ 29 октября 2011

Вариант 1 - идиоматический способ в C ++ 03, он требует большего набора текста, но таков язык.Вариант 2 даже близко не эквивалентен, вы не сможете инициализировать Derived2, так как Base не имеет конструктора по умолчанию, а определение неявно объявленного конструктора по умолчанию для Derived2 необходимоЭто.

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

2 голосов
/ 29 октября 2011

Есть альтернативный подход:

// option 3)
struct Derived3 : Base {
  Derived3(const Base &b) : Base(b) {}
};
Base* baseFactory3() {
    return new Derived3(Base(42,47));
}

Это может не быть хорошей идеей, если создание объекта Full Base стоит дорого или требует внешних ресурсов. В этом случае вы можете создать легкий объект, который несет аргументы конструктора для Base, так что вместо нескольких конструкторов Base у вас есть несколько конструкторов BaseArguments и один конструктор Base, который принимает BaseArguments. Тем не менее, я не думаю, что многие люди будут рассматривать этот хороший стиль в большинстве случаев.

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

Вот вариант вариации паттерна строителя, который я всегда думал, что он может быть полезен, но у меня никогда не было возможности его использовать;Я думаю, вы могли бы назвать это Конструктором.: P

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

Я все еще думаюэто оправдано, только если в вашем классе много конструкторов с разными параметрами.В противном случае это просто раздражение.

class Person
{
  public:
    class Constructor;
};

class Person::Constructor
{
  public:
    Constructor& name(const std::string&);
    Constructor& age(int);
    Person* make();
};

Person* pers = Person::Constructor()
    .name("Bob Marley").make();


class Employee : public Person
{
  public:
    class Constructor;
};

class Employee::Constructor : public Person::Constructor
{
  public:
    Constructor& salary(double);
    Employee* make();
};

Employee* emp = Employee::Constructor()
    .name("Emilly Smith").age(23).make();

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

Я упоминал, что мне не нравится предложенное Ханом решение.Это связано с тем, что (1) вы перемещаете необходимость (пере) объявлять количество конструкторов в дочернем классе на необходимость размещать одинаковое количество функций внутри класса фабрики;(2) это хакерство и намерение не является явным;и (3) вы можете рассматривать это как нарушение внедрения зависимости.

1 голос
/ 29 октября 2011

C ++ 11 поддерживает унаследованные конструкторы.

http://en.wikipedia.org/wiki/C%2B%2B11

Однако не все компиляторы, которые теперь поддерживают подмножество C ++ 11, поддерживают все функции.

Microsoft visual C ++ 2010 поддерживает несколько новых функций C ++ 0x, но не унаследованные конструкторы, вот список: http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx

GCC поддерживает больше новых функций, но не унаследованных конструкторов. Смотри http://gcc.gnu.org/projects/cxx0x.html

Чтобы GCC компилировался с функциями C ++ 0x, вам нужно добавить -std=c++0x или -std=gnu++0x параметр компиляции. В Visual C ++ 2010 они включены по умолчанию.

Вам просто нужно подождать:)

Не существует чистых решений для предыдущей версии стандарта, по этой причине он был представлен в C ++ 11, поскольку это было невозможно сделать с предыдущими версиями. Итак, единственное, что вы можете сделать, это скопировать ваши конструкторы и вызвать базовый конструктор.

...