Для чего используется вызов throw без аргументов?
try{ //.... try { // Call something } catch(const std::exception& ) { // Make/Check something.. throw; // Пересылаем исключение следующему обработчику } //...}catch(const std::exception& e){ std::cout << e.what() << std::endl;} C++: конструктор копирования, оператор присваивания, деструктор Конструктор копирования, деструктор и перегруженный оператор присваивания - незаменимые элементы каждого класса, работающего с динамически выделенной памятью. Конструктор копирования Конструктор копирования, в отличии от других, в качестве параметра принимает константную ссылку на объект класса. // Прототип конструктора копированияKlass(const Klass &);Данный конструктор вызывается всякий раз, когда создаётся новый объект и для его инициализации берётся значение существующего объекта того же типа. Например, в следующих случаях: Klass k2(k1);Klass k3 = k2;Klass k4 = Klass(k3);Klass * pKlass = new Klass(k4);Также конструктор копирования вызывается при передаче объекта в функцию или возврате из неё по значению. Аналогично, с помощью конструктора копирования создаются временные объекты при вычислении арифметических и других операций. В чём же проблема отсутствия конструктора копирования при выделении в классе динамической памяти? Дело в том, что при отсутствии явного описания, он описывается неявно. Неявный конструктор выполняет поверхностное копирование, т. е. просто дублирует биты из переменных. Таким образом, вместо данных из динамической памяти, копируется адреса на них. В результате, появляется несколько объектов, указывающих на одну область памяти. При изменении этой области через один объект, она также изменится и в другом, что в большинстве случаев является нежелательным поведением. Поэтому в классах, работающих с динамической памятью, необходимо всегда явно объявлять конструктор копирования (см. пример в конце). Как вариант исключения данной проблемы, можно поместить конструктор копирования в приватной области класса, что вовсе запретит выполнять копирование. Перегруженная операция присваивания Перегруженная операция присваивания используется при присваивании одного объекта другому существующему объекту. Здесь присутствует такая же проблема, что и в конструкторе копирования. К тому же, у объекта, которому присваивается значение, уже может быть выделена динамическая память. Перед присваиванием новых данных, выделенную ранее память необходимо очистить, чтобы не допустить её утечки (см. пример в конце). Также необходимо обработать случай самоприсваивания. В противном случае, данные в динамической памяти просто будут утеряны. Аналогично копированию, присваивание также можно запретить, поместив операцию в приватной области класса. Деструктор Деструктор вызывается перед удалением объекта и предназначен для освобождения всех используемых ресурсов. Чтобы не допустить утечки памяти, в деструкторе необходимо её очистить. Пример #include <iostream> struct Klass { int size; double * data; Klass() { std::cout << "Конструктор по умолчанию" << std::endl; size = 100; data = new double[size]; } Klass(const Klass & klass) { std::cout << "Конструктор копирования" << std::endl; size = klass.size; data = new double[size]; for (int i = 0; i < size; i++) { data[i] = klass.data[i]; } } Klass & operator=(const Klass & klass) { if (this != &klass) { std::cout << "Перегруженный оператор присваивания" << std::endl; delete[] data; size = klass.size; data = new double[size]; for (int i = 0; i < size; i++) { data[i] = klass.data[i]; } } else { std::cout << "Самоприсваивание" << std::endl; } return *this; } ~Klass() { std::cout << "Деструктор" << std::endl; delete[] data; }}; int main() { setlocale(LC_ALL, "Russian"); Klass k1; Klass k2(k1); Klass k3 = k2; Klass k4 = Klass(k3); Klass * pKlass = new Klass(k4); k1 = k2; k2 = k2;}Стоить отметить, что во всех трёх функциях память должна выделяться и удаляться одинаковым образом. Т. е. нельзя в одном случае использовать delete, а в другом delete[]. Разработка → Приведение типов Qt* Будучи на конференции Qt Developer Days 2010 я узнал, что одним из самых популярных вопросов на собеседовании в разные зарубежные компании, работающие с Qt библиотекой, является вопрос о различиях в способах приведения типов в C++. Поэтому здесь я рассмотрю основные различия между static_cast, dynamic_cast, const_cast, reinterpret_cast, C-style cast, qobject_cast и qvariant_cast
1. static_cast. Синтаксис:
TYPE static_cast<TYPE> (object);
int * p = static_cast<int*>(malloc(100)); Если приведение не удалось, возникнет ошибка на этапе компиляции. Однако, если это приведение между указателями на объекты классов вниз по иерархии и оно не удалось, результат операции undefined. То есть, возможно такое приведение: static_cast<Derived*>(pBase), даже если pBase не указывает на Derived, но программа при этом будет вести себя странно.
2. dynamic_cast Синтаксис:
TYPE& dynamic_cast<TYPE&> (object); TYPE* dynamic_cast<TYPE*> (object);
Используется для динамического приведения типов во время выполнения. В случае неправильного приведения типов для ссылок вызывается исключительная ситуация std::bad_cast, а для указателей будет возвращен 0. Использует систему RTTI (Runtime Type Information). Безопасное приведение типов по иерархии наследования, в том числе для виртуального наследования.
Безопасное приведение по иерархии наследования, в том числе и для виртуального наследования.
Синтаксис:
TYPE const_cast<TYPE> (object);
Пожалуй самое простое приведение типов. Снимает cv qualifiers — const и volatile, то есть константность и отказ от оптимизации компилятором переменной. Это преобразование проверяется на уровне компиляции и в случае ошибки приведения типов будет выдано сообщение. Самое простое приведение типов. Убирает так называемые cv спецификаторы (cv qualifiers), то есть const и volatile. volatile встречается не очень часто, так что более известно как приведение типов, предназначенное для убирания const. Если приведение типов не удалось, выдается ошибка на этапе компиляции.
Синтаксис:
TYPE reinterpret_cast<TYPE> (object);
Самое нахальное приведение типов. Не портируемо, результат может быть некорректным, никаких проверок не делается. Считается, что вы лучше компилятора знаете как на самом деле обстоят дела, а он тихо подчиняется. Не может быть приведено одно значение к другому значению. Обычно используется, чтобы привести указатель к указателю, указатель к целому, целое к указателю. Умеет также работать со ссылками. reinterpret_cast<whatever *>(some *) Чтобы использовать reinterpret_cast нужны очень и очень веские причины. Используется, например, при приведении указателей на функции.
Примеры
unsigned* v_ptr; cout << *static_cast<int*>(v_ptr) <<endl;
Приведение вниз по иерархии:
class Base { public: virtual ~Base(void) { } }; class Derived1 : public Base { }; class Derived2 : public Base { }; class Unrelated { };
Base* pD1 = new Derived1; Вот такое приведение корректно: dynamic_cast<Derived1 *>(pD1);
Derived1 derived1; Unrelated* pUnrelated = reinterpret_cast<Unrelated*>(&derived1);
int* pi; void* vp = pi; char* pch = static_cast<char*>(vp);
float f (float); struct S { float x; float f (float); } s; void g () { reinterpret_cast<int *>(&s.x); reinterpret_cast<void (*) ()>(&f); reinterpret_cast<int S::*>(&S::x); reinterpret_cast<void (S::*) ()>(&S::f); reinterpret_cast<void**>(reinterpret_cast<long>(f)); }
string sHello("Hello"); (void)sHello.size(); // Throw away function return Также я видела использование приведение типов в стиле С для приведения к приватному базовому классу, но для этого можно использовать и reinterpret_cast.
Синтаксис:
TYPE (TYPE*) object;
Си-шный метод приведения типов. Пожалуй самый нежелательный способ приведения типов. Страуструп пишет: С-style cast В языке С явное приведение типов осуществялось при заключении типа в круглые скобки, так называемое "приведение в круглых скобках" Например, преобразование Double --> int: int i = (int) 2.5;Однако, такая форма приведения не наглядна, что существенно затрудняет поиск ошибок Кроме того могут возникать ошибки при приведении пользовательских типов: A * a = ...; B * b = (B *) a;Для некоторых типов могут возникать неопределенности. A и B могут быть никак не связаны, т.е. эта операция может не быть правомощной. Надо различать преобразования связанных типов и несвязанных типов.
Популярное: Почему двоичная система счисления так распространена?: Каждая цифра должна быть как-то представлена на физическом носителе... Как вы ведете себя при стрессе?: Вы можете самостоятельно управлять стрессом! Каждый из нас имеет право и возможность уменьшить его воздействие на нас... ©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (713)
|
Почему 1285321 студент выбрали МегаОбучалку... Система поиска информации Мобильная версия сайта Удобная навигация Нет шокирующей рекламы |