Указатель this в конструкторе
Насколько я понял, когда читал про this, что указатель необходим по большей части, когда мы используем несколько объектов одного и того же класса. Однако встает вопрос, нужно ли в конструкторе по умолчанию или с параметрами присваивать значение через указатель (ниже код примера
". указатель необходим по большей части, когда мы используем несколько объектов одного и того же класса. " - это какая-то ерунда. this - это скрытый параметр каждого нестатического метода класса. На this фундаментально построена вся функциональность классов. Без него - никуда, независимо от того, сколько у нас объектов.
3 ответа 3
Это больше вопрос конвенции кода. this нужно использовать, если имя скрывает другой параметр.
Ну или при работе с шаблонами:
Без this-> не скомпилится, т.к. нужно сообщить явно компилятору, что используется i из A .
Нет, не нужно. Вернее - не обязательно. Это было бы нужно, если бы параметр назывался так же как член класса:
В данном случае надо использовать инициализацию, а не присваивание, тогда this не нужен: A::A(int tmp):tmp(tmp)<> . Ну и конфликта имен нет в Вашем примере, есть сокрытие.
Помимо уже сказанного в других ответах, наличие this-> сразу говорит читателю кода, что речь именно о члене класса, а не о какой-то другой глобальной/локальной переменной. Подобную проблему, правда лучше решать соглашениями по коду, например, начинать все имена членов с префикса m или добавлять суффикс в виде подчеркивания _ . Но и это бывает не нужно, если работать исключительно в современных IDE с достаточным уровнем анализа кода — члены класса будут иметь отдельное от других сущностей выделение цветом/стилем шрифта.
А я знаете, люблю писать this->. Например, мне больше нравится this->swap(other) или std::swap(this->m_number, other->m_number) чем то же самое без this. Конечно, использование this в таких ситуациях абсолютно ни на что не влияет, но без него как будто «обрубки» получаются. Короче, это у кого какой почеркъ
@Jenssen да, мой ответ в основном о стиле кода, нежели о правилах самого языка. Ну и ещё после this-> может сработать автоподстановка в некоторых IDE, что тоже удобно.
При создании экземпляра класса или структуры вызывается его конструктор. Конструкторы имеют имя, совпадающее с именем класса или структуры, и обычно инициализируют члены данных нового объекта.
В следующем примере класс с именем Taxi определяется с помощью простого конструктора. Затем оператор new создает экземпляр этого класса. Конструктор Taxi вызывается оператором new сразу после того, как новому объекту будет выделена память.
Конструктор, который не принимает никаких параметров, называется конструктором без параметров. Конструкторы без параметров вызываются всякий раз, когда создается экземпляр объекта с помощью оператора new , а аргументы в new не передаются. Дополнительные сведения см. в разделе Конструкторы экземпляров.
Создание экземпляров класса можно запретить, сделав конструктор закрытым, следующим образом:
Дополнительные сведения см. в разделе Закрытые конструкторы.
Конструкторы для типов структур похожи на конструкторы классов, но structs не могут содержать явный конструктор без параметров, так как он предоставляется компилятором автоматически. Этот конструктор инициализирует каждое поле в struct со значением по умолчанию. При этом конструктор без параметров вызывается только в том случае, если экземпляр struct создается с помощью переменной new . Например, этот код использует конструктор без параметров, Int32чтобы гарантировать, что целое число инициализируется:
Однако следующий код вызывает ошибку компилятора, так как он не используется new , и потому что он пытается использовать объект, который не был инициализирован:
Кроме того, объекты на основе structs (включая все встроенные числовые типы) можно инициализировать или назначить, а затем использовать, как в следующем примере:
Поэтому вызов конструктора без параметров для типа значения не требуется.
Оба класса и structs могут определять конструкторы, принимающие параметры. Конструкторы, принимающие параметры, необходимо вызывать с помощью оператора new или base. Классы и structs могут определять также несколько конструкторов; для определения конструктора без параметров ни один их них не требуется. Пример:
Этот класс можно создать, воспользовавшись одним из следующих операторов:
Конструктор может использовать ключевое слово base для вызова конструктора базового класса. Пример:
В этом примере конструктор базового класса вызывается перед выполнением соответствующего ему блока. Ключевое слово base можно использовать как с параметрами, так и без них. Любые параметры для конструктора можно использовать как параметры для base или как часть выражения. Дополнительные сведения см. в разделе base.
В производном классе, если конструктор базового класса не вызывается явным образом с помощью base ключевого слова, конструктор без параметров, если он есть, вызывается неявно. Это означает, что следующие объявления конструкторов действуют одинаково:
Если базовый класс не предлагает конструктор без параметров, производный класс должен выполнить явный вызов базового конструктора с помощью base .
Конструктор может вызывать другой конструктор в том же объекте с помощью ключевого слова this. Как и base , this можно использовать с параметрами или без, а все параметры в конструкторе доступны как параметры this или как часть выражения. Например, второй конструктор в предыдущем примере можно переписать, используя this :
Применение ключевого слова this в приведенном выше примере привело к вызову конструктора:
Конструкторы могут иметь пометку public, private, protected, internal, protected internal или private protected. Эти модификаторы доступа определяют, каким образом пользователи класса смогут создавать класс. Дополнительные сведения см. в статье Модификаторы доступа.
Конструктор можно объявить статическим, используя ключевое слово static. Статические конструкторы вызываются автоматически непосредственно перед доступом к статическим полям и обычно используются для инициализации членов статического класса. Дополнительные сведения см. в разделе Статические конструкторы.
Указатель this — это указатель, доступный только в нестатических функциях-членах class struct типа или union типа. Он указывает на объект, для которого вызывается функция-член. Статические функции-члены не имеют указателя this .
Синтаксис
Remarks
Указатель объекта this не является частью самого объекта. Он не отражается в результате sizeof оператора для объекта. Если для объекта вызывается нестатическая функция-член, компилятор передает адрес объекта функции в качестве скрытого аргумента. Например, при вызове следующей функции
можно интерпретировать следующим образом:
Адрес объекта доступен в функции-члене в качестве указателя this . Большинство this вариантов использования указателя являются неявными. Это законно, хотя и ненужно, использовать явный this при обращении к членам .class Пример:
Выражение *this обычно используется для возврата текущего объекта из функции-члена.
Указатель this также используется для защиты от самосохраняющей ссылки:
this Поскольку указатель неизменяем, назначения указателю this запрещены. Более ранние реализации разрешенного назначения this C++ .
this Иногда указатель используется напрямую — например, для управления самонаправленными даннымиstruct, где требуется адрес текущего объекта.
Пример
Тип указателя this
Тип this указателя можно изменить в объявлении функции ключевыми словами и volatile ключевыми const словами. Чтобы объявить функцию с одним из этих атрибутов, добавьте ключевые слова после списка аргументов функции.
Приведенный выше код объявляет функцию-член, X в которой this указатель обрабатывается как const указатель на const объект. Можно использовать сочетания параметров cv-mod-list , но они всегда изменяют объект, на который указывает this указатель, а не сам указатель. Следующее объявление объявляет функцию X , где this указатель является const указателем на const объект:
Тип this функции-члена описывается следующим синтаксисом. Определяется cv-qualifier-list из декларатора функции-члена. Это может быть const или volatile (или и то и другое). class-type — имя :class
[ cv-qualifier-list ] class-type * const this
Другими словами, this указатель всегда является указателем const . Его нельзя переназначить. Квалификаторы const , volatile используемые в объявлении функции-члена, применяются к class экземпляру this , на который указывает указатель в области этой функции.
В следующей таблице приведены дополнительные сведения о том, как работают эти модификаторы.
Семантика модификаторов this
Модификатор | Значение |
---|---|
const | Не удается изменить данные члена; не может вызывать функции-члены, которые не const являются. |
volatile | Данные-члены загружаются из памяти при каждом обращении к ней; отключает определенные оптимизации. |
Ошибка передачи const объекта в функцию-член, которая не const является.
Аналогичным образом, это также ошибка для передачи volatile объекта в функцию-член, которая не volatile является.
Функции-члены, объявленные как const не могут изменять данные-члены, this в таких функциях указатель является указателем на const объект.
Constructors и destructors не могут быть объявлены как const или volatile . Однако их можно вызывать или volatile вызывать const .
В прошлой статье для создания объекта использовался конструктор по умолчанию. Однако мы сами можем определить свои конструкторы. Как правило, конструктор выполняет инициализацию объекта. При этом если в классе определяются свои конструкторы, то он лишается конструктора по умолчанию.
На уровне кода конструктор представляет метод, который называется по имени класса, который может иметь параметры, но для него не надо определять возвращаемый тип. Например, определим в классе Person простейший конструктор:
Конструкторы могут иметь модификаторы, которые указываются перед именем конструктора. Так, в данном случае, чтобы конструктор был доступен вне класса Person, он определен с модификатором public .
Определив конструктор, мы можем вызвать его для создания объекта Person:
В данном случае выражение Person() как раз представляет вызов определенного в классе конструктора (это больше не автоматический конструктор по умолчанию, которого у класса теперь нет). Соответственно при его выполнении на консоли будет выводиться строка "Создание объекта Person"
Подобным образом мы можем определять и другие конструкторы в классе. Например, изменим класс Person следующим образом:
Теперь в классе определено три конструктора, каждый из которых принимает различное количество параметров и устанавливает значения полей класса. И мы можем вызвать один из этих конструкторов для создания объекта класса.
Консольный вывод данной программы:
Ключевое слово this
Ключевое слово this представляет ссылку на текущий экземпляр/объект класса. В каких ситуациях оно нам может пригодиться?
В примере выше во втором и третьем конструкторе параметры называются также, как и поля класса. И чтобы разграничить параметры и поля класса, к полям класса обращение идет через ключевое слово this . Так, в выражении
первая часть - this.name означает, что name - это поле текущего класса, а не название параметра name. Если бы у нас параметры и поля назывались по-разному, то использовать слово this было бы необязательно. Также через ключевое слово this можно обращаться к любому полю или методу.
Цепочка вызова конструкторов
В примере выше определены три конструктора. Все три конструктора выполняют однотипные действия - устанавливают значения полей name и age. Но этих повторяющихся действий могло быть больше. И мы можем не дублировать функциональность конструкторов, а просто обращаться из одного конструктора к другому также через ключевое слово this , передавая нужные значения для параметров:
В данном случае первый конструктор вызывает второй, а второй конструктор вызывает третий. По количеству и типу параметров компилятор узнает, какой именно конструктор вызывается. Например, во втором конструкторе:
идет обращение к третьему конструктору, которому передаются два значения. Причем в начале будет выполняться именно третий конструктор, и только потом код второго конструктора.
Стоит отметить, что в примере выше фактически все конструкторы не определяют каких-то других действий, кроме как передают третьему конструктору некоторые значения. Поэтому в реальности в данном случае проще оставить один конструктор, определив для его параметров значения по умолчанию:
И если при вызове конструктора мы не передаем значение для какого-то параметра, то применяется значение по умолчанию.
Инициализаторы объектов
Для инициализации объектов классов можно применять инициализаторы . Инициализаторы представляют передачу в фигурных скобках значений доступным полям и свойствам объекта:
С помощью инициализатора объектов можно присваивать значения всем доступным полям и свойствам объекта в момент создания. При использовании инициализаторов следует учитывать следующие моменты:
С помощью инициализатора мы можем установить значения только доступных из вне класса полей и свойств объекта. Например, в примере выше поля name и age имеют модификатор доступа public, поэтому они доступны из любой части программы.
Инициализатор выполняется после конструктора, поэтому если и в конструкторе, и в инициализаторе устанавливаются значения одних и тех же полей и свойств, то значения, устанавливаемые в конструкторе, заменяются значениями из инициализатора.
Инициализаторы удобно применять, когда поле или свойство класса представляет другой класс:
Обратите внимание, как устанавливается поле company :
Деконструкторы
Деконструкторы (не путать с деструкторами) позволяют выполнить декомпозицию объекта на отдельные части.
Например, пусть у нас есть следующий класс Person:
В этом случае мы могли бы выполнить декомпозицию объекта Person так:
Значения переменным из деконструктора передаюся по позиции. То есть первое возвращаемое значение в виде параметра personName передается первой переменной - name, второе возващаемое значение - переменной age.
По сути деконструкторы это не более,чем синтаксический сахар. Это все равно, что если бы мы написали:
При получении значений из декоструктора нам необходимо предоставить столько переменных, сколько деконструктор возвращает значений. Однако бывает, что не все эти значения нужны. И вместо возвращаемых значений мы можм использовать прочерк _ . Например, нам надо получить только возраст пользователя:
Поскольку первое возвращаемое значение - это имя пользователя, которое не нужно, в в данном случае вместо переменной прочерк.
В прошлой теме был разработан следующий класс:
И мы можем установить значения для переменных класса Person, можем получить их значения во внешние переменные. Однако если мы попробуем получить значения переменных name и age до их установки, то результаты будут неопределенными:
Чтобы избежать подобной ситуации применяются специальные функции инициализации или конструкторы. Они позволяют инициализировать объект класса. Так, изменим код программы следующим образом:
Теперь в классе Person определен конструктор:
По сути конструктор представляет функцию, которая может принимать параметры и которая должна называться по имени класса. В данном случае конструктор принимает два параметра и передает их значения полям name и age.
Если в классе определены конструкторы, то при создании объекта этого класса необходимо вызвать один из его конструкторов.
Вызов конструктора получает значения для параметров и возвращает объект класса:
После этого вызова у объекта person для поля name будет определено значение "Tom", а для поля age - значение 22. Вполедствии мы также сможем обращаться к этим полям и переустанавливать их значения.
Тажке можно использовать сокращенную форму инициализации:
По сути она будет эквивалетна предыдущей.
Консольный вывод определенной выше программы:
Подобным образом мы можем определить несколько конструкторов и затем их использовать:
В классе Person определено три конструктора, и в функции все эти конструкторы используются для создания объектов:
Хотя пример выше прекрасно работает, однако мы можем заметить, что все три конструктора выполняют фактически одни и те же действия - устанавливают значения переменных name и age. И в C++ можем сократить их определения, вызова из одного конструктора другой и тем самым уменьшить объем кода:
Запись Person(string n): Person(n, 18) представляет вызов конструктора, которому передается значение параметра n и число 18. То есть второй конструктор делегирует действия по инициализации переменных первому конструктору. При этом второй конструктор может дополнительно определять какие-то свои действия.
Таким образом, следующее создание объекта
будет использовать третий конструктор, который в свою очередь вызывает второй конструктор, а тот обращается к первому конструктору.
Инициализация констант и ссылок
В теле конструктора мы можем передать значения переменным класса. Однако константы и ссылки требуют особого отношения. Например, вначале определим следующий класс:
Этот класс не будет компилироваться, так как здесь есть две ошибки - отсутствие инициализации константы name и ссылки ageRef. Хотяя их значения устанавливаются в конструкторе, но к моменту, когда код инструкции из тела конструктора начнут выполняться, константы и ссылки уже должны быть инициализированы. И для этого необходимо использовать списки инициализации:
Списки инициализации представляют перечисления инициализаторов для каждой из переменных и констант через двоеточие после списка параметров конструктора:
Таким образом, все переменные, константы и ссылки получат значение, и никакой ошибки не возникнет.
Читайте также: