Могу ли я вызвать конструктор из другого конструктора (сделать цепочку конструктора) в C ++? - PullRequest
840 голосов
/ 21 ноября 2008

Как разработчик C # , я привык бегать через конструкторы:

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

Есть ли способ сделать это в C ++?

Я пытался назвать имя класса и использовать ключевое слово this, но оба не удаются.

Ответы [ 15 ]

1125 голосов
/ 21 ноября 2008

C ++ 11: Да!

C ++ 11 и далее имеет ту же функцию (называемую делегирующими конструкторами ).

Синтаксис немного отличается от C #:

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};

C ++ 03: Нет

К сожалению, в C ++ 03 нет способа сделать это, но есть два способа симулировать это:

  1. Вы можете объединить два (или более) конструктора через параметры по умолчанию:

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. Используйте метод init для совместного использования общего кода:

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

См. справку по C ++ для справки.

107 голосов
/ 21 ноября 2008

Нет, вы не можете вызывать один конструктор из другого в C ++ 03 (называемый делегирующим конструктором).

Это изменилось в C ++ 11 (он же C ++ 0x), в котором добавлена ​​поддержка следующего синтаксиса:
(пример взят из Википедии )

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};
40 голосов
/ 12 августа 2009

Полагаю, вы можете вызвать конструктор из конструктора. Он скомпилируется и запустится. Недавно я видел, как кто-то делал это, и он работал на Windows и Linux.

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

Ссылка: https://isocpp.org/wiki/faq/ctors#init-methods

22 голосов
/ 22 ноября 2008

Стоит отметить, что вы можете вызывать конструктор родительского класса в вашем конструкторе, например ::100100

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};

Но нет, вы не можете вызвать другой конструктор того же класса.

18 голосов
/ 23 сентября 2011

В C ++ 11 конструктор может вызвать перегрузку другого конструктора :

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};

Кроме того, элементы также могут быть инициализированы следующим образом.

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};

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

12 голосов
/ 24 марта 2011

Если вы хотите быть злым, вы можете использовать новый оператор на месте:

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};

Кажется, работает на меня.

редактировать

Как указывает @ElvedinHamzagic, если Foo содержит объект, который выделил память, этот объект не может быть освобожден. Это еще более усложняет ситуацию.

Более общий пример:

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};

Выглядит немного менее элегантно, наверняка. @ Решение JohnIdol намного лучше.

8 голосов
/ 21 ноября 2008

Нет, в C ++ нельзя вызывать конструктор из конструктора. То, что вы можете сделать, как указал Уоррен, это:

  • Перегрузить конструктор, используя разные подписи
  • Использовать значения по умолчанию для аргументов, чтобы сделать "более простую" версию доступной

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

5 голосов
/ 20 февраля 2012

В Visual C ++ вы также можете использовать эту нотацию внутри конструктора: this-> Classname :: Classname (параметры другого конструктора). Смотрите пример ниже:

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};

Я не знаю, работает ли он где-то еще, я тестировал его только в Visual C ++ 2003 и 2008. Вы можете также вызвать несколько конструкторов таким образом, я полагаю, точно так же, как в Java и C #.

П.С .: Честно говоря, я был удивлен, что это не было упомянуто ранее.

4 голосов
/ 28 октября 2017

Проще говоря, вы не можете раньше C ++ 11.

C ++ 11 представляет делегирующие конструкторы :

Делегирующий конструктор

Если имя самого класса отображается как класс-или-идентификатор в список инициализатора члена, тогда список должен состоять из этого одного члена только инициализатор; такой конструктор известен как делегирование конструктор и конструктор, выбранный единственным членом список инициализатора является целевым конструктором

В этом случае целевой конструктор выбирается из-за перегрузки разрешение и выполняется сначала, затем элемент управления возвращается к делегирующий конструктор и его тело выполняется.

Делегирующие конструкторы не могут быть рекурсивными.

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};

Обратите внимание, что делегирующий конструктор является предложением "все или ничего"; если конструктор делегирует другому конструктору, вызывающему конструктору не разрешается иметь других членов в своем списке инициализации. Это имеет смысл, если вы думаете об инициализации const / reference членов один раз и только один раз.

3 голосов
/ 25 ноября 2011

Еще один вариант, который еще не был показан, - разделить ваш класс на два, обернув легкий интерфейсный класс вокруг исходного класса, чтобы добиться эффекта, который вы ищете:

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...