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

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

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

/**
 * @see https://stackoverflow.com/questions/6991648
 * @see https://stackoverflow.com/questions/6887296
 * @see https://stackoverflow.com/questions/5797965
 */
public class LinePanel extends JPanel {

    private MouseHandler mouseHandler = new MouseHandler();
    private Point p1 = new Point(100, 100);
    private Point p2 = new Point(540, 380);
    private boolean drawing;

    public LinePanel() {
        this.setPreferredSize(new Dimension(640, 480));
        this.addMouseListener(mouseHandler);
        this.addMouseMotionListener(mouseHandler);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.blue);
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(8,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
        g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            drawing = true;
            p1 = e.getPoint();
            p2 = p1;
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            drawing = false;
            p2 = e.getPoint();
            repaint();
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (drawing) {
                p2 = e.getPoint();
                repaint();
            }
        }
    }

    private class ControlPanel extends JPanel {

        private static final int DELTA = 10;

        public ControlPanel() {
            this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
            this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
            this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
            this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
        }

        private class MoveButton extends JButton {

            KeyStroke k;
            int dx, dy;

            public MoveButton(String name, int code, final int dx, final int dy) {
                super(name);
                this.k = KeyStroke.getKeyStroke(code, 0);
                this.dx = dx;
                this.dy = dy;
                this.setAction(new AbstractAction(this.getText()) {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        LinePanel.this.p1.translate(dx, dy);
                        LinePanel.this.p2.translate(dx, dy);
                        LinePanel.this.repaint();
                    }
                });
                ControlPanel.this.getInputMap(
                    WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
                ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        MoveButton.this.doClick();
                    }
                });
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("LinePanel");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.add(new ControlPanel(), BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new LinePanel().display();
            }
        });
    }
}

847
задан einpoklum 22 April 2017 в 10:55
поделиться

8 ответов

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)
    {
      // ...
    }
    

См. запись FAQ C++ для ссылки.

1135
ответ дан Azeem 22 April 2017 в 10:55
поделиться

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

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

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

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

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

1
ответ дан warren 22 April 2017 в 10:55
поделиться

Я бы предложил использовать метод 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();
}

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

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();
}

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

2
ответ дан Peter Mortensen 22 April 2017 в 10:55
поделиться

Нет, в C++ Вы не можете вызвать конструктора от конструктора. Что можно сделать, как садок для кроликов, на который указывают:

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

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

8
ответ дан unwind 22 April 2017 в 10:55
поделиться

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

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

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

edit

Как указывает @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 намного лучше.

12
ответ дан lyngvi 22 April 2017 в 10:55
поделиться

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

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

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

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

22
ответ дан Azeem 22 April 2017 в 10:55
поделиться

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

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

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};
107
ответ дан mskfisher 22 April 2017 в 10:55
поделиться

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

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

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

40
ответ дан 22 November 2019 в 21:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: