Возможно, вы задавались следующими вопросами:
Порядок передачи переменной в функцию?
Как передаётся переменная в функцию?
Создаётся ли временная переменная, при вызове функции?
Данный пример демонстрирует ответ на ваши вопросы.
При вызове функции с передачей объекта по значению, происходит копирование объекта во временную переменную того же типа. При этом вызывается конструктор копирования. Если конструктор копий не найден в этом классе, то выполняется побитовое копирование объекта во временную переменную.
Пример ниже демонстрирует это:
Обратите внимание на то, что отсутсвтие конструктора копий в классе не приводит к вызову определённого в этом классе оператора приваивания (operator =). Чтобы в этом убедиться, закомментируйте конструктор копий:
И теперь вывод программы будет следующим:
Допустим, вы написали класс, который использует указатели, выделяет под них динамическую память, а потом корректно её освобождает по удалению объекта. Всё работает правильно, но вы не реализовали конструктор копирования.
В этом случае вы рискуете встретиться с весьма неявной ошибкой, когда попытаетесь передать объект своего класса в функцию по значению.
Дело в том, что при отсутствии конструктора копий, как было сказано выше, выполняется стандартная операция побитового копирования одного объекта в другой. После копирования указатели в обоих объектах будут иметь одинаковое значение. А это значит, что они будут указывать на одну и ту же область памяти. В самом этом факте ещё нет ничего страшного.
А теперь самое интересное! Когда функция завершается, то удаляются все временные переменные, и в этот момент будет вызван тот самый деструктор вашего класса, который произведёт "правильное" освобождение памяти по имеющемуся указателю. Теперь ваш оригинальный объект, который вы передали ранее в функцию по значению, является разрушенным: его указатель теперь указывает на область памяти, которая уже считается освобождённой.
Ошибка такого рода может создать много проблем, если не следить за конструкторами копирования. Поэтому всегда создавайте правильный конструктор копирования.
В случае с приведённым примером, в конструкторе копирования должна быть выделена новая область памяти для указателя.
Смотрите также:
Порядок выполнения конструктора и деструктора
Порядок выполнения конструктора и деструктора при наследовании
Порядок передачи переменной в функцию?
Как передаётся переменная в функцию?
Создаётся ли временная переменная, при вызове функции?
Данный пример демонстрирует ответ на ваши вопросы.
При вызове функции с передачей объекта по значению, происходит копирование объекта во временную переменную того же типа. При этом вызывается конструктор копирования. Если конструктор копий не найден в этом классе, то выполняется побитовое копирование объекта во временную переменную.
Пример ниже демонстрирует это:
#include <iostream> using namespace std; class A { public: A(int _i) { i = _i; cout << "Constr A i=" << i << endl; } A(A &obj) { i = obj.i + 1; cout << "Copy-Constr A i=" << i << endl; } ~A() { cout << "Destr A i=" << i << endl; } A &operator=(A &obj) { i = obj.i + 2; cout << "Assignment A i=" << i << endl; return *this; } int i; }; void foo(A objA) { cout << "Inside foo(); objA.i = " << objA.i << endl; } int main() { cout << "Begin Program\n"; cout << "\nCreate object A objA(1);\n"; A objA(1); cout << "\nInvoke foo(objA);\n"; foo(objA); cout << "\nEnd Program\n"; return 0; }Вывод:
Begin Program Create object A objA(1); Constr A i=1 Invoke foo(objA); Copy-Constr A i=2 Inside foo(); objA.i = 2 Destr A i=2 End Program Destr A i=1
Обратите внимание на то, что отсутсвтие конструктора копий в классе не приводит к вызову определённого в этом классе оператора приваивания (operator =). Чтобы в этом убедиться, закомментируйте конструктор копий:
//... // A(A &obj) { // i = obj.i + 1; // cout << "Copy-Constr A i=" << i << endl; // } //...Теперь в классе нет конструктора копий, но есть переопределённый оператор присваивания.
//... A &operator=(A &obj) { i = obj.i + 2; cout << "Assignment A i=" << i << endl; return *this; } //...Не смотря на это, оператор присваивания не выполняется при создании временных переменных.
И теперь вывод программы будет следующим:
Begin Program Create object A objA(1); Constr A i=1 Invoke foo(objA); Inside foo(); objA.i = 1 Destr A i=1 // Это удаляется временная переменная! End Program Destr A i=1 // Удаляется переменная из функции main()
Несколько слов о конструкторах копий.
Написание конструкторов копирования для ваших классов - ответственная задача. Особенно если ваш класс работает с указателями.Допустим, вы написали класс, который использует указатели, выделяет под них динамическую память, а потом корректно её освобождает по удалению объекта. Всё работает правильно, но вы не реализовали конструктор копирования.
В этом случае вы рискуете встретиться с весьма неявной ошибкой, когда попытаетесь передать объект своего класса в функцию по значению.
Дело в том, что при отсутствии конструктора копий, как было сказано выше, выполняется стандартная операция побитового копирования одного объекта в другой. После копирования указатели в обоих объектах будут иметь одинаковое значение. А это значит, что они будут указывать на одну и ту же область памяти. В самом этом факте ещё нет ничего страшного.
А теперь самое интересное! Когда функция завершается, то удаляются все временные переменные, и в этот момент будет вызван тот самый деструктор вашего класса, который произведёт "правильное" освобождение памяти по имеющемуся указателю. Теперь ваш оригинальный объект, который вы передали ранее в функцию по значению, является разрушенным: его указатель теперь указывает на область памяти, которая уже считается освобождённой.
Ошибка такого рода может создать много проблем, если не следить за конструкторами копирования. Поэтому всегда создавайте правильный конструктор копирования.
В случае с приведённым примером, в конструкторе копирования должна быть выделена новая область памяти для указателя.
Смотрите также:
Порядок выполнения конструктора и деструктора
Порядок выполнения конструктора и деструктора при наследовании
Комментариев нет:
Отправить комментарий