Анимация
JavaScript
|
Главная Библионтека Задача 14. К порядку! Сложность: 2 у программистов, изучающих С++, часто возникают неправильные представления о том, что можно и чего нельзя делать в С++. В приведенном ниже примере, представленном Яном Кристианом ван Винклем, студенты допускают фундаментальную ошибку - но многие компиляторы пропускают ее даже без предупреждений. Вопрос для новичка 1. Приведенный далее код был действительно написан студентом, изучающим С+ + , и компилятор, которым он пользовался, не выдал никакого предупреждения (более того, так поступает целый ряд популярных компиляторов). Так что же не верно в приведенном коде и почему? #include <string> using namespace std; class A { public: AC const strings, s ) { /* ... */ } stri ng f() { return "hello, world"; } class В : public A { public: B() : A( s = fC) ) {} private: stri ng s; i nt mai nC) { В b; Вопрос для профессионала 2. В каком порядке выполняется инициализация различных частей создаваемого объекта класса в С+ + ? Будьте предельно точны в своем ответе. Укажите порядок инициализации различных частей объекта класса х в следующем примере. class Bl { }; class VI : public Bl { }; class Dl : vi rtual public VI { }; class b2 { }; class B3 { }; class v2 : public Bl, public В2 { }; class d2 : public вЗ, vi rtual public V2 { }; class Ml { }; class m2 { }; class X : public Dl, public D2 { Ml ml ; m2 m2 ; Решение 1. ...Так что же не верно в приведенном коде и почему? пример 14-1 "во : АС S = fО ) {} ... в указанной строке проявляются две взаимосвязанные проблемы, отиосяшисся ко времени жизни объекта и его использованию до создания. Обратите внимание, что выражение s = f () является аргументом конструктора подобьекта базового класса А и, следовательно, выполняется до того, как будет построен базовый подобъскт А (или любая часть объекта в). Во-первых, эта строка кода пытается использовать еще не существующий базовый подобъект А. Компилятор студента, написавшего этот код, не замечал некорректного использования А: :f, состоящего в том, что функция f вызывается для подобъскта д, который еще не сконструирован. Компилятор не обязан диагностировать такие ошибки; однако это то, что называется "качеством реализации", и достаточно хороший компилятор вполне мог бы заметить данную ошибку. Во-вторых, здесь же выполняется попытка использовать член s подобъекта, который еше не существует, т.е. применить оператор присваивания к строке-члену объекта, конструирование которого еще не завершено. 2. В каком порядке выполняется инициализация различных частей создаваемого объекта класса в С++? Будьте предельно точны в своем ответе. Порядок инициализации определяется рекурсивным применением следующего набора правил. • Сначала конструктор последнего производного класса вызывает конструкторы подобъектов виртуальных базовых классов. Инициализация виртуальных базовых классов выполняется в глубину, в порядке слева направо. • Затем конструируются подобъекты н с посредстве иных базовых классов в порядке их объявления в определении класса. • После этого конструируются (нестатические) подобъекты-члены в порядке их объявления в определении класса. • И наконец, выполняется тело конструктора. В качестве примера рассмотрим приведенный в задаче код. Вид наследования (открытое, закрытое или защищенное) не влияет на порядок инициализации, так что все наследование показано мной как открытое. Укажите порядок инициализации различных частей объекта класса X в следующем примере. пример 14-2 class Bl { }; class vl : public Bl { }; class Dl : virtual public vl { }; class 82 { }; class b3 { }; class V2 : public Bl, public в2 { }; class d2 : virtual pub Ii с v2, public B3 { }; class Ml { }; class M2 { }; class X : public Dl, public D2 { Ml ml ; M2 m2 ; Иерархия наследования имеет структуру, показанную иа рис. 14.1. Задача 14. К порядку! 97 Рис. 14.1. Иерархия наследования в примере 14 2 Порядок инициализации объекта х в примере 14-2 имеет следующий вид (каждый показанный ниже вызов конструктора представляет выполнение его тела): • Сначала конструируются виртуальные базовые классы: конструирование vL: В1::в1() vl::vl() конструирование v2: В1::В1() В2::В2() v2::V2() • Затем конструируются невиртуальные базовые классы: конструирование Dl: Dl::D1() конструирование d2: В3::в3() d2::d2() • После этого конструируются члены: м1: :м1С) м2: :м2С) • И наконец, выполняется конструктор X; X: :Х() Резюме Хотя основная цель данной задачи - достичь лучшего понимания порядка конст-руирования объектов (и их деструкции -- в обратном порядке), это не мешает нам повторить одно правило, имеющее некоторое отношение к данной теме. > Рекомендация Не злоупотребляйте наследованием. Если не учитывать отношения дружбы, наследование представляет собой наиболее сильную взаимосвязь, имеющуюся в С+ + , и использовать ее следует только там, где это действительно необходимо. Более подробно этот вопрос рассмотрен в книгах [SutterOO] и [Sutter02]. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [ 27 ] 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |