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




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

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




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

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




Related