Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
Nick Alte
Эксперт С++
1637 / 1009 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
#1

Как инициализировать член раньше предка - C++

12.01.2012, 19:30. Просмотров 602. Ответов 1
Метки нет (Все метки)

Изредка, но может встретиться в жизни такая ситуация, когда надо инициализировать один из членов класса раньше предка. Обычно в том случае, если при инициализации предок ссылается на данные этого члена. Приведу пример:
C++
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
class Foundation { // Фундамент
public:
    Foundation(float Width, float Height);  // Выкопать яму нужных размеров и залить бетоном
private:
    // Нельзя копировать и присваивать
    Foundation(const Foundation&);
    Foundation& operator = (const Foundation&);
};
 
class FloorPlan {  // Чертежи дома
public:
    FloorPlan(const string& name);  // По названию модели дома
    FloorPlan(const FloorPlan&);    // Копировать в принципе можно, но дорого...
    float Width() const;    // Ширина и высота основания
    float Height() const;
private:
    FloorPlan& operator = (const FloorPlan&);
};
 
class Floor { // Этаж
// ...
};
 
class Building: public Foundation { // Здание
private:
    FloorPlan plan;
    vector<Floor> floors;
public:
    Building(const string& name): Foundation(plan.Width(), plan.Height()), plan(name) {ConstructFloors();} // УПС!
    static void ConstructFloors(vector<Floor>& floors, const FloorPlan& plan);
private:
    // Нельзя копировать и присваивать
    Building(const Building&);
    Building& operator = (const Building&);
};
Класс Building наследует от Foundation и содержит чертёж как член. Для создания фундамента надо знать размеры, которые может предоставить чертёж. Но вот досада-то: фундамент является предком и инициализируется раньше всех членов. Так что мы не можем использовать ещё не инициализированный plan, чтобы узнать размеры Foundation. Как быть? Во-первых, можно наследовать Building от FloorPlan и Foundation:
C++
1
2
3
4
5
6
class Building: private FloorPlan, public Foundation {
private:
    vector<Floor> floors;
public:
    Building(const string& name): FloorPlan(name), Foundation(Width(), Height()) {ConstructFloors(floors, *this);}
};
Недостатки метода очевидны: мало того, что мы нарушаем идеологическую чистоту, мы ещё и заливаем в Building интерфейс FloorPlan, что может привести к замусориванию и неожиданным последствиям.

Второй подход проще и очевиднее: надо сначала создать отдельный объект FloorPlan, использовать его для создания Foundation и перекинуть в Building:
C++
1
2
3
4
5
6
7
8
class Building: public Foundation {
private:
    FloorPlan plan;
    vector<Floor> floors;
public:
    Building(const FloorPlan &p): Foundation(p.Width(), p.Height()), plan(p) {ConstructFloors(floors, plan);}
    // Теперь вместо Building d("дача"); надо писать Building d(FloorPlan("дача"));
};
Плохо то, что приходится копировать FloorPlan: это может быть дорогостоящая операция (очереди в мэрии за разрешениями, взятки клеркам, медленный ксерокс). Можно передавать не сам FloorPlan, а указатель на динамически создаваемый объект с передачей владения. Возможности нового стандарта C++11, уже реализованные в последних версиях компиляторов Visual Studio 2010 и GCC позволяет максимально чётко выразить передачу владения и защититься от неприятных последствий при помощи unique_ptr:
C++
1
2
3
4
5
6
7
8
9
class Building: public Foundation {
private:
    std::unique_ptr<const FloorPlan> pplan; // Автоматически уничтожается при уничтожении Building.
    vector<Floor> floors;
public:
    Building(std::unique_ptr<const FloorPlan> p): Foundation(p->Width(), p->Height()), pplan(std::move(p)) {ConstructFloors(floors, *pplan);}
    // Теперь конструирование выглядит ещё веселее:
    // Building d(std::unique_ptr<FloorPlan>(new FloorPlan("дача")));
};
Теперь мы избавились от затратного копирования чертежей, однако неужели нельзя сделать лучше? Разумеется, можно. Тот же unique_ptr основывается на концепции уникального объекта, содержимое которого нельзя копировать, но можно перемещать. Так почему бы не сделать FloorPlan перемещаемым?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class FloorPlan {
public:
    FloorPlan(const string& name); 
    FloorPlan(FloorPlan&& source);  // Перекидываем всё содержимое в this и помечаем source как "пустой"
    float Width() const;
    float Height() const;
};
 
class Building: public Foundation {
private:
    FloorPlan plan;
    vector<Floor> floors;
public:
    Building(FloorPlan &&p): Foundation(p.Width(), p.Height()), plan(std::move(p)) {ConstructFloors(floors, plan);}
    // Мы снова вернулись к Building d(FloorPlan("дача"));
};
Но и этого мало! Здания должны конструироваться с удобством! И при помощи другой особенности С++11, делегации конструкторов, мы можем это сделать:
C++
1
2
3
4
5
6
7
8
9
10
11
class Building: public Foundation {
private:
    FloorPlan plan;
    vector<Floor> floors;
    // Теперь мы спрячем этот конструктор от народа
    Building(FloorPlan &&p): Foundation(p.Width(), p.Height()), plan(p) {ConstructFloors(floors, plan);}
public:
    Building(const string& name): Building(FloorPlan(name)) {} // Просто создаём временный объект FloorPlan и перекидываем создание скрытому конструктору
    // Мы снова вернулись к старому доброму Building d("дача");
private:
};
Добавлю ложку дёгтя в бочку мёда: Visual Studio 2010 делегировать конструкторы не умеет. Поиграться таким фокусом можно пока только в GCC начиная с версии 4.7.
1
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
12.01.2012, 19:30
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Как инициализировать член раньше предка (C++):

Как инициализировать строковой член класса? - C++
Доброго времени. Подскажите, пожалуйста, как инициализировать строковую переменную класса, используя конструктор? #include &lt;iostream&gt; ...

Можно ли инициализировать static член класса функцией? - C++
Сабж. Конкретно - в классе есть static член типа SYSTEM_INFO (Windows.h), а мне нужно его инициализировать с помощью функции GetSystemInfo.

Как сделать чтоб потомок получал данные от предка? - C++
в класс Trend нужно принять данные Vx и Vy из класса MyClass. Компилятор выдает сообщение &quot;компилятор должен иметь тип класса&quot; //...

Как создать массив из объектов разных классов, имеющих общего предка? - C++
Есть основной класc 'Автомобиль' его наследуют 3 класса: мини вен, купе и седан. Как создать массив объектов класса 'автомобиль' что бы...

Одномерные массивы. Найти максимальный член в массиве, начиная со второго член - C++
Как найти максимальный член в массиве, начиная со второго члена?

В последовательности а1,...,a30 поменять местами наибольший член и член с номером m. - C++
Даны натуральное число m, действительные числа а1,..,a30 (числа попарно различны). В последовательности а1,...,a30 поменять местами...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
lemegeton
2924 / 1353 / 135
Регистрация: 29.11.2010
Сообщений: 2,725
13.01.2012, 08:27 #2
Плохой пример. В данной системе классов больше подходит включение, а не наследование.

Например, Здание не логично наследовать от Фундамента или ПланаЭтажа, поскольку здание не ведет себя как фундамент или как план этажа. Тут откровенно напрашивается включение. FloorPlan включен во Floor (1-1), Floor в Building (*-1), Foundation в Building (1-1).
2
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
13.01.2012, 08:27
Привет! Вот еще темы с ответами:

Поменять местами наибольший член последовательности и член с номером m - C++
Помогите в 4 пункте меню сделать вывод на консоль, в файл и защиту если сразу выбрать 4 пункт. #include &lt;stdio.h&gt; #include...

Как объявить указатель на массив через typedef и как инициализировать такой тип - C++
Как заставить заработать этот фрагмент кода? INT_L -- указатель на массив из 100 элементов типа char; Не получается в переменную C...

Если объект константный, означает ли это, что ни один его член-элемент или член-метод не изменится? - C++
Назрел такой вопрос: Если объект константный это означает, что ни один его член-элемент или член-метод не изменится или только некоторые из...

Как инициализировать массивы? - C++
Я пытаюсь сдать проверочную преподавателю , задания он дает легкие , но даже если результат получается тот , который необходим, он хочет ,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru