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

Как разработчик С# я использую для запуска конструкторов:

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

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

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

Есть ли способ сделать это на С++?

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

Ответ 1

С++ 11: Да!

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

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

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

С++ 03: Нет

К сожалению, в С++ 03 нет возможности сделать это, но есть два способа имитации этого:

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

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  • Используйте метод 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)
    {
      // ...
    }
    

См. запись в С++ FAQ для справки.

Ответ 2

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

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

class SomeType
{
  int number;

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

В С++ 11 конструктор

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

В Visual С++ вы также можете использовать эту нотацию внутри конструктора: 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 С++ 2003 и 2008. Вы можете также назвать конструкторы несколько, я полагаю, так же, как в Java и С#.

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

Ответ 9

Если я правильно понял ваш вопрос, вы спрашиваете, можете ли вы вызвать несколько конструкторов в С++?

Если это то, что вы ищете, тогда нет - это невозможно.

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

В конце можно даже иметь один конструктор с аргументами по умолчанию.

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

Ответ 10

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

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

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

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

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

Ответ 11

Я бы предложил использовать метод private friend, который реализует логику приложения конструктора и называется вызываемыми различными конструкторами. Вот пример:

Предположим, что у нас есть класс с именем StreamArrayReader с некоторыми частными полями:

private:
    istream * in;
      // More private fields

И мы хотим определить два конструктора:

public:
    StreamArrayReader(istream * in_stream);
    StreamArrayReader(char * filepath);
    // More constructors...

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

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    StreamArrayReader(&instream);
    instream.close();
}

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

private:
  friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

Теперь этот метод (потому что он является другом) имеет доступ к закрытым полям o. Тогда первый конструктор будет:

StreamArrayReader::StreamArrayReader(istream * is) {
    init_stream_array_reader(this, is);
}

Обратите внимание, что это не создает несколько копий для вновь созданных копий. Второй:

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    init_stream_array_reader(this, &instream);
    instream.close();
}

То есть вместо того, чтобы один конструктор вызывал другого, оба вызывали частного друга!

Ответ 12

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

Ответ 13

Этот подход может работать для некоторых классов (когда оператор присваивания ведет себя "хорошо" ):

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}

Ответ 14

Проще говоря, вы не можете до С++ 11.

С++ 11 вводит делегирование конструкторов:

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

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

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

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

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

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

Ответ 15

Было бы проще проверить, чем решить:) Попробуйте следующее:

#include <iostream>

class A {
public:
    A( int a) : m_a(a) {
        std::cout << "A::Ctor" << std::endl;    
    }
    ~A() {
        std::cout << "A::dtor" << std::endl;    
    }
public:
    int m_a;
};

class B : public A {
public:
    B( int a, int b) : m_b(b), A(a) {}
public:
    int m_b;
};

int main() {
    B b(9, 6);
    std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;    
    return 0;
}

и скомпилируйте его с помощью 98 std:   g++ main.cpp -std = С++ 98 -o test_1

вы увидите:

A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor

так:)