необычный конструктор C ++ [дубликат]

Вот почему динамически созданные элементы не реагируют на клики & nbsp;:

var body = $("body");
var btns = $("button");
var btnB = $("<button>B</button>");
// `<button>B</button>` is not yet in the document.
// Thus, `$("button")` gives `[<button>A</button>]`.
// Only `<button>A</button>` gets a click listener.
btns.on("click", function () {
  console.log(this);
});
// Too late for `<button>B</button>`...
body.append(btnB);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

В качестве обходного пути вы должны прослушивать все клики и проверять исходный элемент & nbsp;:

var body = $("body");
var btnB = $("<button>B</button>");
var btnC = $("<button>C</button>");
// Listen to all clicks and
// check if the source element
// is a `<button></button>`.
body.on("click", function (ev) {
  if ($(ev.target).is("button")) {
    console.log(ev.target);
  }
});
// Now you can add any number
// of `<button></button>`.
body.append(btnB);
body.append(btnC);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

Это называется «Event Delegation». Хорошие новости, это встроенная функция в jQuery: -)

var i = 11;
var body = $("body");
body.on("click", "button", function () {
  var letter = (i++).toString(36).toUpperCase();
  body.append($("<button>" + letter + "</button>"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>

267
задан Taryn 22 March 2017 в 17:25
поделиться

12 ответов

Это список инициализации члена . Вы должны найти информацию об этом в любой хорошей книге C ++ .

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

Точка выгрузки из пункта часто задаваемых вопросов состоит в том, что

При прочих равных условиях ваш код будет работать быстрее, если вы используете списки инициализации, а не назначение.

163
ответ дан TallChuck 18 August 2018 в 19:06
поделиться
  • 1
    Знание терминологии имеет решающее значение - я ревную, что не думал об этом. – Mark Ransom 11 November 2009 в 00:36
  • 2
    Существует также множество других причин для использования списков инициализации. особенно когда имеет место порядок инициализации. Жаль, что у него такой глупый поддельный синтаксис вызова функции. – Martin Beckett 11 November 2009 в 00:50
  • 3
    @mgb, список инициализации не определяет порядок инициализации. Элементы-члены инициализируются в том порядке, в котором они объявлены в классе, даже если это отличается от порядка инициализации конструктора. – ScottJ 11 November 2009 в 01:26
  • 4
    @mgb: Я не думаю, что он предназначен как синтаксис поддельных функций. Это синтаксис инициализации, например int i(23);, std::vector<double> emptyVec(0);, std::vector<double> fullVec(10,23.); и т. Д. Только с удаленным типом, конечно, потому что тип находится в объявлении члена. – Steve Jessop 11 November 2009 в 02:03
  • 5
    @Martin: У него нет синтаксиса вызова функции, у него есть синтаксис построения (ala: new String (& quot; Name & quot;)). Он вписывается в конструктор лучше, чем Foo (int num): m_Count = 5. Не говоря уже о том, что классы должны быть построены в этой точке в любом случае, так как он инициализирован здесь. Foo (int num): Bar = num, не будет компилироваться правильно. Кажется странным видеть Foo (int num): m_Count (num), поскольку примитивные типы не построены. – Lee Louviere 22 April 2011 в 21:33

Вы правы, это действительно способ инициализации переменных-членов. Я не уверен, что для этого есть много пользы, кроме как ясно выражая, что это инициализация. Наличие «bar = num» внутри кода может быть легко перемещено, удалено или неверно истолковано.

5
ответ дан Aric TenEyck 18 August 2018 в 19:06
поделиться
  • 1
    Преимущество в том, что оно обычно более эффективно. И в некоторых случаях, например, когда у вас есть const переменные-члены или переменные-члены, являющиеся ссылками, вы имеете , чтобы использовать список инициализаторов. – Charles Salvia 11 November 2009 в 00:35

Другой уже объяснил вам, что синтаксис, который вы наблюдаете, называется «список инициализаторов конструктора». Этот синтаксис позволяет настраивать базовые подобъекты и субобъекты-члены класса (в отличие от разрешения инициализации по умолчанию или оставаться неинициализированным).

Я просто хочу отметить, что синтаксис, который, как вы сказали, «выглядит как вызов конструктора», не обязательно является вызовом конструктора. В языке C ++ синтаксис () - это всего лишь одна стандартная форма синтаксиса инициализации . Это интерпретируется по-разному для разных типов. Для типов классов с определяемым пользователем конструктором это означает одно (это действительно вызов конструктора), для типов классов без определяемого пользователем конструктора это означает другое (так называемая инициализация значения ) для пустого ()), а для типов, не относящихся к классу, он снова означает что-то другое (так как типы некласса не имеют конструкторов).

В вашем случае член данных имеет тип int. int не является типом класса, поэтому он не имеет конструктора. Для типа int этот синтаксис означает просто «инициализировать bar со значением num» и все. Это делается так же, как и непосредственно, никаких конструкторов, поскольку еще раз int не является типом класса, поэтому он не может иметь никаких конструкторов.

8
ответ дан Destructor 18 August 2018 в 19:06
поделиться
  • 1
    Но bjarne stroustrup в своей книге TC ++ PL & amp; Язык программирования C ++ говорит, что & quot; Встроенные типы также имеют конструкторы по умолчанию & quot; . geeksforgeeks.org/c-default-constructor-built-in-types & amp; informit.com/guides/content.aspx?g=cplusplus&seqNum=15 также говорит, что встроенные типы имеют конструкторы . Я лично задал этот вопрос bjarne по почте и amp; он говорит мне, что да, встроенные типы также имеют конструкторы. так что ваш ответ неправильный !!! – Destructor 11 May 2016 в 04:54
  • 2
    @Destructor: Мой ответ абсолютно правильный. Бьярне Страуструпп намеренно и явно лгал в своей книге ради его упрощения. Сравните размер книги TC ++ PL и размер языка C ++. Увидеть разницу? Цена за относительную компактность TC ++ PL такая очевидная (и хорошо известная каждому) ошибка и упущения, как вы упомянули (есть и немало других). Итак, чтобы выразить это более кратко: мой ответ прав, TC ++ PL ошибочен. Но для такого человека, как вы, который только начинает учиться, TC ++ PL достаточно хорош. – AnT 11 May 2016 в 14:43
  • 3
    И, пожалуйста, не рассказывайте нам троллические сказки о «задании этого вопроса бьярне по почте». Этот вопрос, опять же, хорошо известен и исчерпывающе обсуждается и закрывается давным-давно. (Но даже если бы он сказал вам что-то подобное, в любом случае это не имеет значения.) В конце концов, единственное, что имеет значение, - это то, что говорит спецификация языка. То, что говорит Бьярне Страуструп, не имеет значения. Сообщение о geeksforgeeks, которое вы связали, является полностью фиктивным. – AnT 11 May 2016 в 14:52
  • 4
    Итак, вы считаете, что стандарт является ошибкой? В стандарте C ++ также есть ошибки. – Destructor 11 May 2016 в 14:54
  • 5
    @Destructor: Конечно, это так. Однако стандартный определяет язык. Все, что говорится в стандарте, - это Абсолютная Истина по определению. Единственные "ошибки" возможно, это в основном связанные с формулировкой вещи, такие как противоречивые формулировки, двусмысленные формулировки, недоопределенные и т. д. Такие ошибки яростно разыскиваются, сообщаются, документируются, обсуждаются и разрешаются. & Quot; Ошибки & Quot; в намерении также могут существовать, но они делятся спорами. – AnT 11 May 2016 в 15:26
Foo(int num): bar(num)    

Эта конструкция называется списком инициализаторов членов в C ++.

Проще говоря, она инициализирует ваш член bar значением num.


В чем разница между инициализацией и присваиванием внутри конструктора?

Инициализация члена:

Foo(int num): bar(num) {};

Назначение членов:

Foo(int num)
{
   bar = num;
}

Существует значительная разница между Инициализацией члена с использованием списка инициализаторов членов и присвоением ему значения внутри тела конструктора.

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

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

Как вы видите, есть дополнительные накладные расходы на создание & amp; назначение в последнем, что может быть значительным для пользовательских классов.

Cost of Member Initialization = Object Construction 
Cost of Member Assignment = Object Construction + Assignment

Последнее фактически эквивалентно:

Foo(int num) : bar() {bar = num;}

Хотя первое эквивалентно простому:

Foo(int num): bar(num){}

Для встроенных (ваш пример кода) или членов класса POD практических накладных расходов нет.


Когда вам нужно использовать список инициализаторов Member?

У вас будет (довольно принудительно) использовать список инициализатора члена, если:

  • В вашем классе есть ссылочный элемент
  • В вашем классе есть нестатический член const или
  • У вашего члена класса нет конструктора по умолчанию или
  • Для инициализации членов базового класса или
  • Когда имя параметра конструктора совпадает с именем элемента данных ( это действительно НЕ ДОЛЖНО)

Пример кода:

class MyClass
{
    public:
        //Reference member, has to be Initialized in Member Initializer List
        int &i;       
        int b;
        //Non static const member, must be Initialized in Member Initializer List
        const int k;  

    //Constructor’s parameter name b is same as class data member 
    //Other way is to use this->b to refer to data member
    MyClass(int a, int b, int c):i(a),b(b),k(c)
    {
         //Without Member Initializer
         //this->b = b;
    }
};

class MyClass2:public MyClass
{
    public:
        int p;
        int q;
        MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
        {
        }

};

int main()
{
    int x = 10;
    int y = 20;
    int z = 30;
    MyClass obj(x,y,z);

    int l = 40;
    int m = 50;
    MyClass2 obj2(x,y,z,l,m);

    return 0;
}
  • MyClass2 не имеет конструктора по умолчанию, поэтому он должен быть инициализирован через список инициализаторов членов.
  • Базовый класс MyClass не имеет конструктора по умолчанию, поэтому для инициализации его члену-члену нужно будет использовать список инициализаторов членов.

Онлайн-версия образца кода .


Важные моменты для примечания при использовании Списки инициализаторов членов:

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

Они не инициализируются в том порядке, в котором они указаны в Список членов Initalizer. Короче говоря, список инициализации Member не определяет порядок инициализации.

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

256
ответ дан hichris123 18 August 2018 в 19:06
поделиться
  • 1
    @nils Это лучший ответ. Описанный порядок инициализации Als также чрезвычайно важен, в то время как компилятор Visual Studio ничего не скажет об этом, другой компилятор, такой как gcc, потерпит неудачу. Также важно отметить, в зависимости от вашего компилятора и ситуации не всегда true , что это улучшит производительность или станет более эффективным. – ForceMagic 14 March 2012 в 09:21
  • 2
    Если у меня есть конструктор с большим количеством переменных, который говорит более 10, неудобно ли перечислить все из них в этом списке? – ryf9059 7 January 2013 в 14:06
  • 3
    @ ryf9059: Почему вы думаете, что это будет неудобно? Вы должны их перечислить в любом случае, так почему бы не упорядочить то же, что и объявление. – Alok Save 7 January 2013 в 14:10
  • 4
    Вы можете уточнить некоторые из ваших терминов, этот вопрос был задан, чтобы прояснить некоторые детали вашего ответа. Детали несколько ортогональны первоначальному вопросу, заданному здесь. – Shafik Yaghmour 7 July 2015 в 18:44
  • 5
    это должен был быть ответ. Слава богу, я прокрутил вниз, иначе я бы пропустил это. – Coffee_lover 13 August 2015 в 11:41

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

Рассмотрим эти два примера:

// Example 1
Foo(Bar b)
{
   bar = b;
}

// Example 2
Foo(Bar b)
   : bar(b)
{
}

В примере 1:

Bar bar();  // default constructor
bar = b;  // assignment

В примере 2:

Bar bar(b) // copy constructor

Все дело в эффективности.

14
ответ дан Josh 18 August 2018 в 19:06
поделиться
  • 1
    Я бы не сказал, что речь идет об эффективности. Речь идет о предоставлении способа инициализации того, что требует инициализации, но не может быть инициализировано по умолчанию. По некоторым причинам люди упоминают константы и ссылки в качестве примеров, в то время как наиболее очевидным примером могут быть классы без конструкторов по умолчанию. – AnT 11 November 2009 в 01:26
  • 2
    Мы оба правы; в его примере вы можете утверждать об эффективности; для конструкции конструктора const / reference / no default это как эффективность, так и необходимость. Из-за этого я оставил ответ ниже. [Голос Фарнсворта] Он может делать другие вещи. Почему бы и нет? – Josh 11 November 2009 в 05:19
  • 3
    Bar bar(); // default constructor Ты уверен? – Lightness Races in Orbit 27 December 2015 в 15:55

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

11
ответ дан Lightness Races in Orbit 18 August 2018 в 19:06
поделиться
  • 1
    Этот ответ неверен. Это не «альтернативный способ инициализации членов класса»; это возможный способ only (ну, кроме нового механизма NSDMI). невозможно "инициализировать" члены в корпусе конструктора; в этот момент вы назначаете . – Lightness Races in Orbit 1 May 2013 в 11:02
  • 2
    @LightnessRacesinOrbit - полученная точка, но константа или точка ссылки все еще действительны в моем ответе. – LeopardSkinPillBoxHat 1 May 2013 в 11:19
  • 3
    Да, почти. Он должен сказать, что «члены класса, которые являются константами или ссылками , должны быть инициализированы». (полная остановка) – Lightness Races in Orbit 1 May 2013 в 11:49

Еще не упоминается в этом потоке: начиная с C ++ 11, в списке инициализаторов членов может использоваться инициализация списка (ака. «равномерная инициализация», «согласованная инициализация»):

Foo(int num): bar{num} {}

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

1
ответ дан M.M 18 August 2018 в 19:06
поделиться

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

7
ответ дан Mark Ransom 18 August 2018 в 19:06
поделиться
  • 1
    Написанная на одной строке внутри объявления так, что ее просто невозможно определить как список инициализации – Martin Beckett 11 November 2009 в 00:51

Это список инициализации. Он инициализирует элементы до того, как будет запущен тело конструктора. Рассмотрим

class Foo {
 public:
   string str;
   Foo(string &p)
   {
      str = p;
   };
 };

vs

class Foo {
public:
  string str;
  Foo(string &p): str(p) {};
};

. В первом примере str будет инициализироваться конструктором без аргументов

string();

перед телом конструктора Foo. Внутри конструктора foo

string& operator=( const string& s );

будет вызываться на 'str', как вы делаете str = p;

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

string( const string& s );

с «p» в качестве аргумента.

6
ответ дан nos 18 August 2018 в 19:06
поделиться

существует другое «преимущество»

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

5
ответ дан pm100 18 August 2018 в 19:06
поделиться

Это список инициализации для конструктора. Вместо построения по умолчанию x, y и z, а затем присваивая им значения, полученные в параметрах, эти члены будут инициализированы этими значениями сразу с места в карьер. Это может показаться не очень полезным для float s, но это может быть довольно многопользовательский режим с настраиваемыми классами, которые дороги построить.

5
ответ дан suszterpatt 18 August 2018 в 19:06
поделиться

Это не скрыто, это синтаксис списка инициализации C ++

В принципе, в вашем случае x будет инициализирован _x, y с _y, z с _z.

9
ответ дан wkl 18 August 2018 в 19:06
поделиться
  • 1
    Обновление из будущего: эта ссылка сейчас нарушена. (Это приводит к странице «Доступ запрещен»). – Edward 4 February 2016 в 16:45
  • 2
    @Edward thanks - я обновил ссылку, чтобы она снова указывала где-то снова. – wkl 4 February 2016 в 17:58
Другие вопросы по тегам:

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