Мне задавали этот вопрос на собеседовании, и даже после его составления я до сих пор не понимаю результата …
У меня есть следующий класс:
class Point
{
public:
Point(double x = 0, double y = 0) { m_X = x; m_Y = y; }
double m_X, m_Y;
};
и main.cpp это:
int main()
{
double a = 1, r = 1, xCoord = 5, yCoord = 7;
Point p = (a+r*xCoord , a+r*yCoord);
cout<<"X = "<<p.m_X<<" Y = "<<p.m_Y<<endl;
return 0;
}
Члены данных класса p получают значения:
m_X = a+r*yCoord, m_Y = 0
Теперь, почему это?
Из-за оператора запятой и неявного конструктора. Выражение (a + r * xCoord , a + r * yCoord)
является приложением оператора запятой и имеет значение a + r * yCoord
и теперь однопараметрическая неявная форма вашего Point
конструктор используется с этим значением, а второй параметр по умолчанию.
Вы можете предотвратить появление таких ошибок, сделав конструктор explicit
, который обычно рекомендуется для любого конструктора, который может быть вызван с одним аргументом, если вы действительно не хотите неявных преобразований.
Чтобы получить желаемое поведение, вы хотите прямую инициализацию:
Point p(a + r * xCoord, a + r * yCoord);
Это вопрос с подвохом … выражение
(a+r*xCoord , a+r*yCoord)
это comma operator
выражение, так что ваш код на самом деле
Point p = a+r*yCoord;
Конструктор может использоваться как конструктор преобразования (потому что не explicit
) и, следовательно, это то же самое, что и запись:
Point p(a+r*yCoord, 0);
Чтобы получить «ожидаемый результат», вы должны удалить знак равенства и написать
Point p(a+r*xCoord , a+r*yCoord);
В общем случае лучше использовать два разных конструктора, а не полагаться на аргументы по умолчанию, например, с помощью:
struct P2d {
double x, y;
P2d() : x(0), y(0) { }
P2d(double x, double y) : x(x), y(y) { }
};
Вы всегда должны обращать особое внимание на конструкторы, принимающие только один параметр (или которому вы можете передать только один параметр, как в вашем примере), потому что, если они не объявлены explicit
C ++ может использовать их неявно для преобразований, иногда удивительным образом.
Последнее замечание заключается в том, что странно, что оператор запятой, использованный в коде, не выдал предупреждение, потому что первое выражение не имеет побочных эффектов, и компилятор должен быть в состоянии обнаружить, что это, вероятно, было ошибкой.
Point p = (a+r*xCoord , a+r*yCoord);
(Выражение) использует comma operator
, который оценивает все условия по порядку, возвращение самого правого.
Правильные формы будут
auto p = Point(a+r*xCoord , a+r*yCoord);
Point p(a+r*xCoord , a+r*yCoord);
Вы можете изменить
Point p = (a+r*xCoord , a+r*yCoord);
в
Point p (a+r*xCoord , a+r*yCoord);
Первый использует оператор запятой, чтобы дать вам один значение (последнее в выражении оператора запятой), которое вы затем назначаете своему объекту, и это установит ваш x
член к нему и по умолчанию ваш y
член к нулю.
Последний вызовет конструктор с двумя аргументами.
Это потому, что вы используете оператор преобразования, double
в
Point
, При использовании инициализации копирования (инициализация с
=
знак), выражение справа от =
первый
преобразован в тип слева, а затем конструктор копирования
используется (концептуально, по крайней мере). Там нет никакого способа указать
более одного аргумента с использованием инициализации копирования. Так что ваши
выражение справа (a + r*xCoord, a + r*yCoord)
, А также
запятая это запятая оператор (не запятая),
который оценивает свой левый аргумент, а затем правую
аргумент, и имеет для результатов результаты его правой руки
аргумент. (И результаты левого аргумента брошены
далеко.)