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

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

Войти
Регистрация
Восстановить пароль
Другие темы раздела
C++ Вычислить и вывести на экран в виде таблицы значения функции http://www.cyberforum.ru/cpp-beginners/thread427003.html
Не пойму гдето ошибка, не хочет считать по второй формуле помогите пожалуйста... условия: Вычислить и вывести на экран в виде таблицы значения функции F в интервале Xнач до Xкон c шагом dX. ...
C++ в каком направлении действовать при написании программы дано задание: "Проверить, является ли выражение, состоящее только из прописных букв заданной строки, палиндромом. Если да, то напечатать полученный палиндром. В противном случае вывести строку,... http://www.cyberforum.ru/cpp-beginners/thread427001.html
Вычисление интеграла C++
задача такая Вычислить с точностью\varepsilon интеграл ,где y=f(x) - прямая проходящая через точки A(c,d) и B - точку минимума \int_{a}^{b}\sin (f1^2(x))dx ...
Сумма ряда. Число повторений цикла. Нужна проверка и помощь C++
Текст задания находится во вложении. Сумма ряда и выражение для проверки не сходятся, хотя сделано по аналогичному примеру. Не могу разобраться в чем ошибка. Помогите пожалуйста) ...
C++ Поиск позиции символа в строке http://www.cyberforum.ru/cpp-beginners/thread426976.html
Здравствуйте, у меня есть строка со словом и строка с прочерками (--------), длина у них одинаковая, есть кнопки, на которых буквы алфавита. Мне нужно, чтоб при нажатии на любую из кнопок...
C++ Задача на массив символьных строк. Возможно я не первый кто просит помочь в данной задачи, но все же повторюсь.: Дан текст, состоящий из n предложений. Предложение представляет собой арифметическое выражение. Создать массив,... подробнее

Показать сообщение отдельно
Nick Alte
Эксперт С++
1640 / 1012 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1

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

12.01.2012, 19:30. Просмотров 612. Ответов 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
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru