Явное присвоение против неявного присвоения
Я читаю учебник для С++, но на самом деле это не давало мне разницы (помимо синтаксиса) между ними. Вот цитата из учебника.
Вы также можете присваивать значения своим переменным при объявлении. Когда мы присваивать значения переменной с помощью оператора присваивания (равно знак), его называют явным присваиванием:
int nValue = 5; // explicit assignment
Вы также можете присваивать значения переменным, используя неявное присвоение:
int nValue(5); // implicit assignment
Несмотря на то, что неявные присваивания очень похожи на вызовы функций, компилятор отслеживает, какие имена являются переменными, а какие функции, чтобы они могли быть правильно решены.
Есть ли разница? Является ли еще предпочтительнее другого?
Ответы
Ответ 1
Первый предпочтительнее с примитивными типами, такими как int
; второй - с типами, у которых есть конструктор, потому что он вызывает явное выражение конструктора.
Например, если вы определили class Foo
, который может быть создан из одного int
, тогда
Foo x(5);
предпочтительнее
Foo x = 5;
(Вам нужен прежний синтаксис, когда передано более одного аргумента, если вы не используете Foo x = Foo(5, "hello");
, который является уродливым и выглядит как operator=
.)
Ответ 2
Для примитивных типов оба эквивалентны, для пользовательских типов классов существует разница. В обоих случаях исполняемый код будет таким же (после выполнения основных оптимизаций), но требования к типам различаются, если элемент, из которого мы инициализируемся, не относится к типу, который мы создаем.
Инициализация копирования (T t = u;
) эквивалентна построению копирования из временного типа T
, который неявно преобразован из u
в T
. С другой стороны, прямая инициализация эквивалентна прямому вызову соответствующего конструктора.
В то время как в большинстве случаев не будет никакой разницы, если конструктор, который принимает u
, объявлен explicit
или если конструктор копирования недоступен, то инициализация копирования не будет выполнена:
struct A {
explicit A( int ) {}
};
struct B {
B( int ) {}
private:
B( B const & );
};
int main() {
A a(1); // ok
B b(1); // ok
// A a2 = 1; // error: cannot convert from int to A
// B b2 = 1; // error: B( B const & ) is not accessible
}
Для некоторого исторического фона изначально примитивные типы должны были инициализироваться с помощью инициализации копирования. Когда * initializer-list * s были добавлены к языку для инициализации атрибутов участника из класса, было решено, что примитивные типы должны быть инициализированы с тем же синтаксисом, что классы, чтобы синтаксис в списке инициализаторов был единообразным и простым. В то же время, разрешая инициализацию классов с помощью инициализации копирования, пользовательские типы приближаются к примитивным типам. Разумеется, различия в двух форматах инициализации: int a = 5.0;
обрабатывается как преобразование от 5.0
до int
, а затем инициализация a
из int
. То же самое происходит с определенными пользователем типами: T u = v;
обрабатывается как преобразование из v
в T
, а затем копирует конструкцию u
из этого преобразованного значения.
Ответ 3
когда вы объявляете переменную и инициализируете ее, они функционально одинаковы в этом контексте. Я обычно отношусь к двум следующим образом:
int nValue = 5; // assignment syntax
и
int nValue(5); // construction syntax
Для базовых типов я предпочитаю назначение по конструкции, поскольку это более естественно, особенно для тех, кто запрограммирован на других языках.
Для типов классов я предпочитаю конструктивный синтаксис, поскольку он устраняет существование функции-конструктора.