C++ конструктор вопрос

В программировании C++ для абсолютного Новичка, 2-ой книги издания, было следующее утверждение:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

Это равно:

HeapPoint::HeapPoint(int x, int y) { thePoint = new Point(x,y); }

И, поскольку мы делаем это в конструкторе, какие значения назначены для x а также y? Должны ли мы писать значения, основанные на x а также y в new Point(x,y)? Или это правильно?

ОБНОВЛЕНИЕ: я думаю, что у меня появилась идея инициализации x а также y Как и в книге, в функции есть следующее:

HeapPoint myHeapPoint(2,4);

6 ответов

Обычно вы должны предпочесть первую конструкцию, т.е. использовать список инициализаторов.

Вторая конструкция предпочтительнее, если

  1. Вы хотите попробовать.. поймать вокруг этого или
  2. У вас есть несколько из них, и вы собираетесь хранить их как обычные указатели, и в этом случае вам нужно остерегаться того, что одна из ваших новостей может потерпеть неудачу, и вам нужно будет выполнить некоторую очистку. Вы бы справились с этим, используя какой-то тип auto_ptr / unique_ptr, пока не узнаете, что все распределения выполнены успешно, а затем выпустите их. Это потому, что вы предположительно удаляете их в своем деструкторе, но ваш деструктор не будет вызван, если конструктор сгенерирует.

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

Когда они не одинаковы?

  1. Если thePoint Это своего рода умный указатель, первая форма инициализирует его, передавая новую точку своему конструктору, тогда как вторая форма, вероятно, инициализирует ее нулем, а затем передает новую точку своему оператору присваивания.
  2. когда thePoint является одной из нескольких переменных-членов, первая форма инициализирует ее в том порядке, в котором она указана в определении класса, тогда как вторая форма будет присваиваться в теле конструктора. Таким образом, порядок, в котором вещи становятся "гидратированными", может варьироваться между двумя формами, что может иметь значение, если переменные некоторых членов зависят от других.

Если вы новичок, то, скорее всего, для вас это бессмысленно. Но не беспокойся об этом. Эти различия очень тонки и почти никогда не будут иметь значения. (Я уверен, что есть и другие исключения, но ни одно из них не приходит на ум сейчас.)

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

Эти две формы не совсем одинаковы в том смысле, что список инициализации дает значение для помещения непосредственно в переменную, тогда как в противном случае переменные инициализируются по умолчанию, если это уместно (например, std::strings по умолчанию пусты, но int, double и т.д. переменные-члены имеют фактически случайные значения), а затем перезаписываются при обнаружении операции присваивания. Эта перезапись может быть менее эффективной, но она также может быть недопустимой для некоторых константных и ссылочных переменных-членов, которые нельзя изменять после их создания. В общем, по возможности используйте список инициализаторов, и вы не ошибетесь. Только когда вы обнаружите, что вы еще не можете что-то инициализировать, потому что сначала вам нужно выполнить некоторые другие шаги, вы должны поместить явный оператор присваивания внутри тела конструктора. Помните, что инициализация происходит в том порядке, в котором переменные-члены объявлены в классе, независимо от того, в каком порядке вы перечислили их в самом конструкторе: хороший компилятор (например, GCC) может предупредить вас об этом.

Оба одинаковы. Первый случай:

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

использует конструктор класса, к которому thePoint принадлежит и указывает на новую выделенную память для Point,

Во втором случае также назначается память, а ее адрес назначается thePoint

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

Так:

  • в вашем первом случае ваш член thePoint непосредственно построен с thePoint(new Point(x,y))

  • во втором случае он сначала выделяется, вероятно, потому, что для него доступен конструктор по умолчанию, затем созданный объект переопределяется вызовом, выполненным в теле (да, тот же, но не в том же месте)!! Таким образом, вы потеряли эффективность здесь.

Если существуют списки инициализации, это по веским причинам (C++ - очень точный язык, синтаксис действительно строг, за исключением ужасных вещей, происходящих из C, все логично)! Например, если ваш класс использует ссылочный член, вы должны будете использовать список инициализации, так как ваш объект не будет полностью построен:

class A
{
public:
    B& _b;
    C& _c;

    A(B& b, C& c):_b(b), _c(c) // compiles !
    {

    }

    A(B& b, C& c)
    {
        _b(b); // does not compile !
        _c(c); // does not compile !
    }
}

Имейте в виду, что здесь, если бы мы сделали _c(c), _b(b) в 1-м конструкторе (обратный порядок), копировать конструкторы классов B а также C в любом случае будет вызываться в том же порядке: они вызываются в порядке, в котором определены члены (т.е. _b до _c), а не порядок, вы их пишете!!!!

Первое не совпадает со вторым.

в этом конкретном случае они, вероятно, дадут тот же результат. однако Point мог бы легко реализовать оператор присваивания для new Point и делать что-то "другое" (у меня нет книги, поэтому я не знаю всех деталей). Кроме того, оператор присваивания должен делать то, что вы ожидаете... однако, thePoint может быть контейнером (например, умный указатель), который может (по какой-то странной причине) вести себя по-разному при использовании initialization(Point) против default initialization followed by assignment,

эти детали, скорее всего, не будут иметь значения в этом случае, но они влияют на порядок инициализации и выполнение. разница будет важна, когда ваши программы будут расти. в то время инициализация займет время, и вы захотите убедиться, что объекты инициализированы правильно: что они построены правильно (в первый раз) и что они построены в правильном порядке. Наиболее очевидный случай: будет иметь значение, когда конструктор по умолчанию ведет себя не так, как конструктор с параметрами, особенно когда конструктор производит распределение или имеет другие длительные (или поведенчески разные) побочные эффекты.

И, поскольку мы делаем это в конструкторе, какие значения присваиваются для int x и int y?

это полностью зависит от Pointконструктор.

Должны ли мы записывать значения, введенные из x и y в новой точке (x,y)? Или это правильно?

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

HeapPoint::HeapPoint(int x, int y): thePoint(new Point(x,y)) { }

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

const Point* const thePoint;

первый const означает, что вы не можете изменить точку (например, Point.x или же Point.y). Второй констант означает, что вы не можете назначить новое распределение для переменной. тривиальные примеры для примера в OP, но определенно полезные по мере роста ваших программ.

Другие вопросы по тегам