Анимация
JavaScript


Главная  Библионтека 

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

Задача 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