[C++] Можно ли вызвать конструктор из другого конструктора (сделать цепочку конструктора) в C ++?


Answers

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

Это было изменено в C ++ 11 (aka C ++ 0x), который добавил поддержку следующего синтаксиса:
(пример из Wikipedia )

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};
Question

Как разработчик 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», но оба они терпят неудачу.




Проще говоря, вы не можете до 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)
};

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




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




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

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

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

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




В 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 #.

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




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

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

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

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

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




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

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 намного лучше.




Links