Как передать результат метода в качестве параметра конструктору базового класса в C ++? - PullRequest
2 голосов
/ 17 декабря 2008

Я пытаюсь добиться чего-то вроде этого:

class Base
{
  public:

  Base(string S) 
  {
  ...
  };
}

class Derived: Base
{
public:
  int foo;
  string bar()
  {
    return stringof(foo); // actually, something more complex
  };

  Derived(int f) : foo(f), Base(bar()) 
  {
  };
}

Теперь, это не работает так, как я хочу, потому что bar () вызывается в конструкторе Derived до инициализации foo.

Я подумал о добавлении статической функции, аналогичной bar (), которая принимает foo в качестве параметра, - и использовал ее в списке инициализации, но подумал, что могу спросить, есть ли другие методы, которые можно использовать, чтобы вытащить себя этот ...

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

class Derived: Base
{
public:
  int foo;

  static string bar(int f)
  {
    return stringof(f); // actually, something more complex
  }

  string bar()
  {
    return bar(foo); 
  };

  Derived(int f) :  Base(bar(f)) , foo(f)
  {
  };
}

Ответы [ 6 ]

4 голосов
/ 17 декабря 2008

Вы можете вызывать только статические функции в списке инициализатора. Как у вас это в вашем коде:

class Derived: Base
{
public:
  int foo;
  string bar()
  {
    return stringof(foo); // actually, something more complex
  };

  Derived(int f) : foo(f), Base(bar()) 
  {
  };
}

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

  1. Сначала все виртуальные базовые классы
  2. Тогда не виртуальные базовые классы в порядке их появления в списке базовых классов
  3. Тогда все объекты-члены в том порядке, в котором они определены в определении класса.

Таким образом, вы в итоге вызываете stringof с неинициализированным значением. Эта проблема решена в boost::base_from_member. Также обратите внимание, что вызов любой нестатической функции-члена до завершения всех инициализаторов конструктора всех базовых классов является неопределенным поведением.

Однако вызов статических функций вполне подходит:

class Derived: Base
{
public:
  int foo;
  static string bar(int f)
  {
    return stringof(f); // actually, something more complex
  };

  Derived(int f) : Base(bar(f)), foo(f)
  {
  };
}
4 голосов
/ 17 декабря 2008

Да, использование функции (метод статического класса или обычной функции), которая принимает foo в качестве параметра и возвращает строку, является хорошим решением. Вы можете вызвать эту же функцию из Derived :: bar, чтобы предотвратить дублирование кода. Итак, ваш конструктор будет выглядеть так:

Derived(int f) : Base(stringof(f)), foo(f) {}

Я помещаю вызов конструктора Base первым в списке, чтобы подчеркнуть порядок, в котором происходят инициализации. Упорядочение списка инициализаторов не имеет никакого эффекта, поскольку все члены класса инициализируются в том порядке, в котором они объявлены в теле класса.

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

class Base {
public:
    Base(string S) {  ...  }
    void bat() { ... }
};

class Derived {
    Base *base;
    int foo;

public:
    Derived(int f) : base(NULL), foo(f) {
        base = new Base(bar());
    }
    ~Derived() {
        delete base;
    }

    string bar() {
        return stringof(foo); // actually, something more complex
    }

    void bat() {
        base->bat();
    }
};

Вам нужно будет рассмотреть плюсы и минусы для вашей конкретной ситуации. С Derived, имеющим ссылку на Base, вы получаете больший контроль над порядком инициализации.

2 голосов
/ 17 декабря 2008

Конструктор базового класса всегда вызывается перед инициализацией других членов производного класса; ваш компилятор должен предупреждать вас о неправильной последовательности инициализаторов. Единственное правильное решение - сделать bar() статическим методом, который принимает f в качестве параметра.

1 голос
/ 17 декабря 2008

Конструктор предназначен для построения объекта. Это означает, что до тех пор, пока он не вернется, там не будет объекта, и, следовательно, вызов функций-членов просто не будет работать надежно. Как и все остальные, используйте статическую функцию или функцию, не являющуюся членом.

0 голосов
/ 17 декабря 2008

Просто переместите код конструктора в функцию Initialize () и вызовите его из конструктора. Это намного проще, чем статическое / нестатическое переопределение или что-то в этом роде.

0 голосов
/ 17 декабря 2008

Я тоже хотел это сделать, но в конце концов сдался.
В качестве параметра Base () можно использовать любой подходящий вызов функции.
Другой вариант - добавить в Base альтернативный конструктор, который принимает int и выполняет преобразование в саму строку.

...