[ 20 ] :: КлассыУверен, что у каждого свои обоснованные представления о том, какими должны быть классы. Вот, например, взять наследование. Каким оно должно быть? Занимаясь «научным» программированием, мне вообще не приходилось им пользоваться ни разу. Занимаясь промышленным программированием, к наследованию приходилось прибегать довольно часто. Однако даже там private и protected наследования мне ни разу не попадались. Могу ли я из этого сделать далеко идущие выводы об их бесполезности? Нет. Для этого нужны данные и большой опыт работы в области разработки ПО.
В высокоуровневом программировании (в моих задачах) классы используются не на полную мощность и всего лишь как удобство для некоторой рутинной работы и возможность избежать дублирования разных конструкций. Там главная задача – оптимизировать лишь 10-20% кода, а остальное лишь бы просто работало.
В разработке ПО же главным является простота и безопасность конструкций. Поэтому инкапсуляция, например, играет важную роль в безопасности, полиморфизм и наследование позволяют упростить код. Хотя, конечно, можно по-разному смотреть на эти вещи. Нужны ли они в HPC, я не знаю. Нужно ли следовать в HPC тем же принципам, что используются в разработке ПО, я тоже не знаю. Раньше я, например, вообще обходился только функциями и в редких случаях структурами для объединения переменных в одну группу. Такие вещи, как сокрытие данных вообще не имели смысла, потому что я никогда не делал ошибок, типичных для разработки ПО, не только в силу опыта, но и потому что для подобных ошибок просто не было почвы. Я не мог ошибиться и испортить значения по неконстантной ссылке или попортить член класса, обращаясь к нему напрямую, а не через функцию типа get / set, у меня не было необходимости дублировать код, потому что объектов с похожими свойствами в программе просто не возникало, а ошибок, связанных с тем, что «здесь исправил а тут забыл» не могло быть в принципе, потому что неудачная в чём-либо программа не исправлялась, а безжалостно удалялась, чтобы можно было написать новую без соблазна скопировать ошибочную (в плане идеи, а не реализации) конструкцию. А вот с разработкой ПО всё сложнее. Там, наоборот, думать надо о простоте и безопасности, барахтаясь в болоте рутины и примитивных одинаковых конструкций. Случайно ошибиться в силу объёма кода может даже профессионал выше моего уровня (и обязательно сделает это), поэтому всякие императивы ООП нужны ему в достаточной мере.
Возможно, в HCP всё-таки нужны не только программы, решающие конкретные задачи, но и полноценное ПО, поэтому язык должен поддерживать стандартные элементы ООП, однако поскольку я их использовал на практике редко, высказаться о классах могу лишь отчасти.
Собирая свой небольшой опыт работы с классами в кучу могу сказать следующее. Мне кажется, что все данные классов всегда должны быть скрыты, обратиться к ним можно только через функции.
Далее, нужен способ обращения к внутренним данным класса, который не порождал бы конфликтов имён. Например, наиболее частая проблема:
Код:
class Star {
public:
Star ( int x_, int y_ ) : x(x_), y(y_) { }
private:
int x, y;
};
Если кого-то совершенно не раздражает описанная в этом примере проблема, то я могу только порадоваться за него, но лично мне такие люди не встречались. В нормальном языке такого быть не должно. Не знаю, как вы, но я прямо-таки чувствую в этой проблеме типичную недоработку проектирования. Казалось бы, выходом могло бы стать создание пространства имён над всеми переменными, но вот ведь незадача! - нельзя делать пространство имён внутри класса. Класс внутри класса тоже не вариант, думаю, не надо пояснять, почему.
Порадовало, что появились ключевые слова override и final. Что-то похожее должно быть обязательно.
Также радует, что можно теперь запретить функции, которые компилятор создаёт по умолчанию (помечать их как «delete»). Такая штуковина тоже полезна. По крайней мере, если я хочу запретить создавать объект конструктором по умолчанию, мне теперь не нужно прятать его в private-область (тело-то для него всё равно нужно определять).
Далее, при наследовании нужно учесть возможность переопределять НЕ все виртуальные функции. Если я по какой-то причине не хочу переопределять функцию из базового класса, я не обязан это делать. Не знаю как сейчас, но раньше это было ошибкой, когда такие функции остались без тела. Вообще, мне кажется полезным сообщать об ошибке, связанной с отсутствием тела функции, только тогда, когда это тело действительно нужно, то есть откуда-то вызывается.
Несколько удручала меня раньше проблема, при которой я не мог вызвать конструктор с параметром для массива объектов одного класса. Как-то так:
Код:
Star * stars = new Star [ 10 ] ( 5, 6 ) // Создать 10 звёзд с координатами (5,6).
Или, что было бы ещё чудеснее:
Код:
/* Создать 10 звёзд с координатами (0,0), (1,1) и т. д. */
Star * stars = new Star [ 10 ] ( [] ( int index ) -> (int, int) { return (index, index); } )
Разумеется, я вовсе не настаиваю на подобном синтаксисе. Я лишь хочу сказать, что при создании массива объектов иногда хочется использовать конструктор с параметрами для каждого объекта. Правда же я не один такой требовательный?
Иногда требуется создать одну и ту же функцию-член в двух экземплярах: константную и неконстантную:
Код:
const int & operator [ ] ( int index ) const { return data [ index ]; }
int & operator [ ] ( int index ) { return data [ index ]; }
Нет разницы между этими функциями. Но делать их нужно две и хоть ты тресни. А бывает, что тело функции имеет несколько больший размер и налицо явное дублирование кода. Можно извратиться и сделать всё через макросы, но куда удобнее писать на языке без применения костылей.
Иногда нужна серия функций типа get/set, для управления скрытыми переменными. Мне кажется, в большинстве случаев их можно как-то автоматически генерировать.
Нужны ли какие-то специальные виды классов для особого применения (типа синглотонов, делегатов и т. д.), я тоже не знаю. В C++ эти штуки вполне делаются штатными методами.
И последнее, то есть не самое главное. С точки зрения полного понимания идеи сокрытия данных нельзя прописывать названия переменных внутри объявления класса в заголовочном файле. Однако вынести эти переменные в другой файл без лишних довольно существенных телодвижений тоже нельзя. От этого тоже веет какой-то недоработкой.
Каких-то особенных нестандартных идей по классам у меня сейчас нет.
[ Продолжение следует ]