Я бы хотел уточнить некоторые моменты, связанные с вызовом конструктора копии и обычного конструктора. У меня есть класс, содержащий указатель на динамически выделяемую память. Я включил в конструктор и деструктор соответствующие блоки. Также перегрузил оператор присваивания и явно написал конструктор копии чтобы избежать побитового копирования по умолчанию. Ввел необходимые арифметические операции как члены класса:
classname classname::operator+(const classname &right) {
classname temp(param);
...
return temp;
}
classname classname::operator*(const classname &);//реализация аналогично +
Насколько я понимаю при обращении объекту класса первый раз вызывается конструктор, а при выходе из области его видимости — деструктор. Просто протестировал следующую строчку:
classname A(param), B(param), C(param);
A=B*C+A;
И посмотрел в каком порядке вызываются кон/деструкторы. Оказалось что деструкторы вызываются не сразу после выхода из функции перегруженного оператора и использовании программой возвращаемого им значения а только после обработки знака равенства. Вот в этот момент и вызываются все необходимые деструкторы для удаления промежуточных результатов вычислений.
На эту в общем-то логичную штуку наткнулся после попытки выяснить когда же все-таки вызывается конструктор копии объекта. Два варианта понятны, а именно: когда производится инициализация свежеобъявленного объекта класса другим объектом (classname A=B), и когда имеет место передача объекта в функцию по значению. Но ещё он может вызываться при возвращении копии локального объекта, объявленного в какой-нибудь функции. Идея понятна: локальный умирает при выходе из функции а копия возвращается. Правда бывает так не всегда. В частности в моей функции умножения он вызывается, а в функции сложения нет (деструктор вызывается при обработке знака равенства). Хотелось бы уточнить когда же все-таки он вызывается и от чего это зависит... Это был вопрос. :)
И второй вопрос по поводу перегрузки операторов.
Можно ли каким-нибудь хитрым ходом сделать перегрузку псевдооператора [][] (двойной массив), которого нет, через [], который есть. Вариант [x,y] понятен но хотелось бы чтобы сохранился вид [x][y]. Это тоже был вопрос к многоуважаемому сообществу программистов. :)
JV>И посмотрел в каком порядке вызываются кон/деструкторы. Оказалось что деструкторы вызываются не сразу после выхода из функции перегруженного оператора и использовании программой возвращаемого им значения а только после обработки знака равенства.
Правильно, деструкотр присваиваемого объекта обязан вызывать после операции, в данном случае присваивания.
Если было бы не так, тогда что бы мы присваивали бы после работы деструктора ?
JV>Вот в этот момент и вызываются все необходимые деструкторы для удаления промежуточных результатов вычислений. JV>На эту в общем-то логичную штуку наткнулся после попытки выяснить когда же все-таки вызывается конструктор копии объекта.
Компилятор может оптимизировать и считать твой объект classname temp(param) в оператора * или + тем временным объектом в который расчитываеться результат операции * или +.
JV>Можно ли каким-нибудь хитрым ходом сделать перегрузку псевдооператора [][] (двойной массив), которого нет, через [], который есть. Вариант [x,y] понятен но хотелось бы чтобы сохранился вид [x][y]. Это тоже был вопрос к многоуважаемому сообществу программистов.
Можно сделать промежуточный класс который будет возвращать оператор[] твоего класса, и который сам будет иметь оператор[].
Например:
class YourClass
{
int a[5][5];
class Tmp
{
YourClass &your_class;
int i;
Tmp(YourClass &your_class_, int i_): your_class(your_class_), i(i_)
{
}
Tmp(const Tmp &);
friend class YourClass;
public:
int &operator[](int j)
{
return your_class.a[i][j];
}
};
public:
Tmp operator[](int i)
{
return Tmp(*this, i);
}
};
int main()
{
YourClass y;
y[3][4]=5;
int m=y[3][4];
}
Здравствуйте, Jamais Vu, Вы писали:
JV>Хотелось бы уточнить когда же все-таки он вызывается и от чего это зависит... Это был вопрос.
Он вызывается после того, как объект перестал быть нужен (и по крайней мере до начала следующей инструкции, если на какой-либо временный объект не была заведена константная ссылка). Подробности смотри в Стандарте (12.2).
Еще надо помнить, что компилятор имеет право соптимизировать возвращение локальной переменной по значению, обойдясь без копирования.
JV>И второй вопрос по поводу перегрузки операторов. JV>Можно ли каким-нибудь хитрым ходом сделать перегрузку псевдооператора [][] (двойной массив), которого нет, через [], который есть.
тебе нужен один оператор[], который вернет объект некоего левого класса, в котором будет храниться, например, данный х и ссылка на матрицу, а у него определить свой оператор[], который уже будет возвращать элемент.
JV>Вариант [x,y] понятен но хотелось бы чтобы сохранился вид [x][y]. Это тоже был вопрос к многоуважаемому сообществу программистов.
он, конечно, понятен, но в С++ невозможен, ибо оператор[] воспринимает только один аргумент.
Если ты хочешь писать два индекса в скобках, переопредели оператор().
Особенно за перегрузку [][]. Попробую.
А вот вызов деструктора после операции присваивания это видимо особенность компиллятора VC++6. Потому что даже в более длинной цепочке операций, скажем:
A=B+C*D-F+G;
деструкторы промежуточных результатов вызываются только после обработки =, а не после использования возвращаемого значения в следующей операции.
To` раз спасибо за консультацию.
Здравствуйте, Jamais Vu, Вы писали:
JV>деструкторы промежуточных результатов вызываются только после обработки =, а не после использования возвращаемого значения в следующей операции.
Правило очень простое: временные объекты разрушаются по завершении вычисления полного выражения, при вычислении которого они были созданы, в порядке, обратном порядку их создания.
Здравствуйте, comer, Вы писали:
C>Можно сделать промежуточный класс который будет возвращать оператор[] твоего класса, и который сам будет иметь оператор[].
...
Если скорость играет решающую роль, то можно сделать еще вот так:
template <typename T>
class myarr
{
T* m_data;
int m_nColumns;
int m_nRows;//Нужен только для контроля корректности индексовpublic:
...
T* operator[] (size_t nRow) { assert(nRow < m_nRows); return m_data + nRow*m_nColumns; }
};
минус этого подхода — невозможно проверить второй индекс в выражении [][]