Это может быть глупый вопрос, но все же мне немного любопытно …
Недавно я работал над одним из моих проектов бывших коллег, и я заметил, что он действительно любил использовать что-то вроде этого:
int foo(7);
вместо:
int foo = 7;
Это нормальный / хороший способ сделать это на языке C ++?
Есть ли какая-то польза от этого? (Или это просто какой-то глупый стиль программирования, которым он увлекался?)
Это действительно напоминает мне хороший способ, как переменные-члены класса могут быть назначены в конструкторе класса … что-то вроде этого:
class MyClass
{
public:
MyClass(int foo) : mFoo(foo)
{ }
private:
int mFoo;
};
вместо этого:
class MyClass
{
public:
MyClass(int foo)
{
mFoo = foo;
}
private:
int mFoo;
};
Для основных типов нет никакой разницы. Используйте то, что согласуется с существующим кодом и выглядит более естественным для вас.
Иначе,
A a(x);
выполняет прямая инициализация, а также
A a = x;
выполняет инициализация копии.
Вторая часть — это список инициализирующих членов, есть куча Q&Как об этом на StackOverflow.
Оба действительны. Для встроенных типов они делают то же самое; для типов классов есть небольшая разница.
MyClass m(7); // uses MyClass(int)
MyClass n = 3; // uses MyClass(int) to create a temporary object,
// then uses MyClass(const MyClass&) to copy the
// temporary object into n
Очевидное значение заключается в том, что если MyClass
не имеет конструктора копирования, или он есть, но он недоступен, попытка создания завершается неудачей. Если конструкция будет успешной, компилятору будет позволено пропустить конструктор копирования и использовать MyClass(int)
непосредственно.
Все ответы выше верны. Просто добавьте к этому, что C ++ 11 поддерживает другой способ, как говорят, для инициализации переменных.
int a = {2} ;
или же
int a {2} ;
Несколько других хороших ответов указывают на разницу между конструированием «на месте» (ClassType v(<constructor args>)
) и создание временного объекта и использование конструктора копирования для его копирования (ClassType v = <constructor arg>
). Я думаю, что нужно сделать еще два замечания. Во-первых, вторая форма, очевидно, имеет только один аргумент, поэтому, если ваш конструктор принимает более одного аргумента, вы должны предпочесть первую форму (да, есть способы обойти это, но я думаю, что прямая конструкция более краткая и удобочитаемая — но , как уже было отмечено, это личное предпочтение).
Во-вторых, форма, которую вы используете, имеет значение, если ваш конструктор копирования делает что-то значительно отличающееся от стандартного конструктора. В большинстве случаев этого не будет, и некоторые будут утверждать, что это плохая идея, но язык допускает, что это так (все сюрпризы, с которыми вы сталкиваетесь из-за этого, тем не менее, Вы сами виноваты)
Это стиль инициализации переменных в C ++ — C ++ добавил его для фундаментальных типов, чтобы одну и ту же форму можно было использовать для фундаментальных и пользовательских типов. это может быть очень важно для кода шаблона, который предназначен для создания экземпляров любого типа.
Желаете ли вы использовать его для нормальной инициализации основных типов — это предпочтение стиля.
Обратите внимание, что C ++ 11 также добавляет единый синтаксис инициализации который позволяет использовать один и тот же стиль инициализации для всех типов — даже для агрегатов, таких как структуры и массивы POD (хотя для пользовательских типов может потребоваться новый тип конструктора, который принимает список инициализации, чтобы позволить использовать с ними унифицированный синтаксис ).
Ваш вопрос вовсе не глупый, так как все не так просто, как может показаться. Предположим, у вас есть:
class A {
public:
A() {}
};
а также
class B {
public:
class B(A const &) {}
};
Пишу
B b = B(A());
Требует, чтобы конструктор копирования B был доступен. Пишу
B b = A();
Требуется также, чтобы преобразователь B преобразовал B (A const &не быть объявленным явным. С другой стороны, если вы напишите
A a;
B b(a);
все хорошо, но если пишешь
B b(A());
Это интерпретируется компилятором как объявление функции b, которая принимает безымянный аргумент, который является функцией без параметров, возвращающей A, что приводит к таинственным ошибкам. Это известно как C ++ самый неприятный анализ.
Я предпочитаю использовать стиль в скобках … хотя я всегда использую пробел, чтобы отличать вызовы функций или методов, в которых я не использую пробел:
int foo (7); // initialization
myVector.push_back(7); // method call
Одна из причин, по которой я предпочитаю использовать это для всех целей инициализации, заключается в том, что это помогает напомнить людям, что это не назначение. Следовательно, перегрузки для оператора присваивания не будут применяться:
#include <iostream>
class Bar {
private:
int value;
public:
Bar (int value) : value (value) {
std::cout << "code path A" << "\n";
}
Bar& operator=(int right) {
value = right;
std::cout << "code path B" << "\n";
return *this;
}
};
int main() {
Bar b = 7;
b = 7;
return 0;
}
Выход:
code path A
code path B
Такое ощущение, что наличие знака равенства скрывает разницу. Даже если это «общеизвестно», мне нравится, чтобы инициализация выглядела заметно иначе, чем назначение, так как мы можем это сделать.
Это просто синтаксис для инициализации чего-либо:
SomeClass data(12, 134);
Это выглядит разумно, но
int data(123);
Выглядит странно, но они имеют одинаковый синтаксис.