Каков правильный способ C # для представления структуры данных ...
Remeber: «Все модели ошибочны, но некоторые из них полезны». - George EP Box
Нет «правильного пути», только полезный.
Выберите тот, который вам полезен и / ваши пользователи , Вот и все. Развивайте экономически, не переучивайте. Чем меньше кода вы пишете, тем меньше кода вам нужно будет отлаживать. (читайте следующие выпуски).
- Отредактировано
. Мой лучший ответ был бы ... это зависит. Наследование из списка приведет к тому, что клиенты этого класса будут подвергнуты методам, которые могут не отображаться, прежде всего потому, что FootballTeam выглядит как бизнес-объект.
- Издание 2
Я искренне не помню, что я имел в виду в комментарии «не переучивайтесь». Хотя я считаю, что мышление KISS является хорошим ориентиром, я хочу подчеркнуть, что наследование бизнес-класса из списка создаст больше проблем, чем устраняет, из-за утечки абстракции .
С другой стороны, я считаю, что существует ограниченное число случаев, когда просто наследовать из Списка полезно. Как я писал в предыдущем издании, все зависит. Ответ на каждый случай сильно зависит от знания, опыта и личных предпочтений.
Спасибо @kai за то, что он помог мне более точно рассказать о ответе.
я задаюсь вопросом, возможно ли реализовать отложенные вычисления в C++ разумным способом. Если да, как Вы сделали бы это?
Да, это возможно и довольно часто сделанное, например, для матричных вычислений. Основной механизм для упрощения этого является перегрузкой оператора. Рассмотрите случай матричного дополнения. Подпись функции обычно выглядела бы примерно так:
matrix operator +(matrix const& a, matrix const& b);
Теперь, для создания этой функции ленивой достаточно возвратить прокси вместо фактического результата:
struct matrix_add;
matrix_add operator +(matrix const& a, matrix const& b) {
return matrix_add(a, b);
}
Теперь все, что должно быть сделано, должно записать этот прокси:
struct matrix_add {
matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }
operator matrix() const {
matrix result;
// Do the addition.
return result;
}
private:
matrix const& a, b;
};
волшебство находится в методе operator matrix()
, который является неявным оператором преобразования от matrix_add
до плоскости matrix
. Таким образом, можно объединить несколько операций в цепочку (путем обеспечения соответствующих перегрузок, конечно). Оценка происходит только, когда конечный результат присвоен matrix
экземпляр.
РЕДАКТИРОВАНИЕ я должен был быть более явным. Как это, код не имеет никакого смысла, потому что, хотя оценка происходит лениво, это все еще происходит в том же выражении. В частности, другое дополнение оценит этот код, если matrix_add
структура не будет изменена для разрешения последовательного сложения. C++ 0x значительно упрощает это путем разрешения variadic шаблонов (т.е. шаблонные списки переменной длины).
Однако один очень простой случай, где этот код на самом деле обладал бы реальным, прямым преимуществом, следующий:
int value = (A + B)(2, 3);
Здесь, предполагается, что A
и B
двумерные матрицы и что разыменование сделано в нотации Фортрана, т.е. вышеупомянутое вычисляет одно элемент из матричной суммы. Конечно, расточительно добавить целые матрицы. matrix_add
к спасению:
struct matrix_add {
// … yadda, yadda, yadda …
int operator ()(unsigned int x, unsigned int y) {
// Calculate *just one* element:
return a(x, y) + b(x, y);
}
};
за Другими примерами недалеко ходить. Я только что помнил, что реализовал что-то связанное недавно. В основном я должен был реализовать строковый класс, который должен придерживаться фиксированного, предопределенного интерфейса. Однако мой конкретный строковый класс имел дело с огромными строками, которые не были на самом деле сохранены в памяти. Обычно, пользователь просто получил бы доступ к маленьким подстрокам от исходной строки с помощью функции infix
. Я перегрузил эту функцию для своего строкового типа для возврата прокси, который содержал ссылку на мою строку, наряду с желаемым запуском и конечным положением. Только то, когда эта подстрока на самом деле использовалась, сделало она запрашивает API C для получения этой части строки.
Повышение. Лямбда очень хороша, но Повышение. Первичный точно , что Вы ищете. Это уже имеет перегрузки весь операторы C++, которые по умолчанию выполняют их обычную функцию, когда proto::eval()
назван, но может быть изменен.
То, что уже объяснил Konrad, может быть помещено далее для поддержки вложенных вызовов операторов, все выполняемые лениво. В примере Konrad у него есть объект выражения, который может сохранить точно два аргумента точно для двух операндов одной операции. Проблема состоит в том, что это только выполнится одно подвыражение лениво, которое приятно объясняет понятие в отложенных вычислениях, помещенных простыми словами, но не улучшает производительность существенно. Другой пример показывает также хорошо, как можно применяться operator()
для добавления только некоторых элементов с помощью того объекта выражения. Но оценить произвольные сложные выражения, нам нужен некоторый механизм, который может хранилище структура этого также. Мы не можем обойти шаблоны, чтобы сделать это. И название этого expression templates
. Идея состоит в том, что один шаблонный объект выражения может сохранить структуру некоторого произвольного подвыражения рекурсивно, как дерево, где операции являются узлами, и операнды являются дочерними узлами. Для очень хорошее объяснение я просто нашел сегодня (спустя несколько дней после того, как я записал ниже кода), см. здесь .
template<typename Lhs, typename Rhs>
struct AddOp {
Lhs const& lhs;
Rhs const& rhs;
AddOp(Lhs const& lhs, Rhs const& rhs):lhs(lhs), rhs(rhs) {
// empty body
}
Lhs const& get_lhs() const { return lhs; }
Rhs const& get_rhs() const { return rhs; }
};
, Который сохранит любую операцию сложения, даже вложенную один, как видно по следующему определению оператора + для простого типа точки:
struct Point { int x, y; };
// add expression template with point at the right
template<typename Lhs, typename Rhs> AddOp<AddOp<Lhs, Rhs>, Point>
operator+(AddOp<Lhs, Rhs> const& lhs, Point const& p) {
return AddOp<AddOp<Lhs, Rhs>, Point>(lhs, p);
}
// add expression template with point at the left
template<typename Lhs, typename Rhs> AddOp< Point, AddOp<Lhs, Rhs> >
operator+(Point const& p, AddOp<Lhs, Rhs> const& rhs) {
return AddOp< Point, AddOp<Lhs, Rhs> >(p, rhs);
}
// add two points, yield a expression template
AddOp< Point, Point >
operator+(Point const& lhs, Point const& rhs) {
return AddOp<Point, Point>(lhs, rhs);
}
Теперь, если Вы имеете
Point p1 = { 1, 2 }, p2 = { 3, 4 }, p3 = { 5, 6 };
p1 + (p2 + p3); // returns AddOp< Point, AddOp<Point, Point> >
, теперь просто необходимо перегрузить оператор = и добавить, что подходящий конструктор для Точки вводит и принимает AddOp. Измените его определение:
struct Point {
int x, y;
Point(int x = 0, int y = 0):x(x), y(y) { }
template<typename Lhs, typename Rhs>
Point(AddOp<Lhs, Rhs> const& op) {
x = op.get_x();
y = op.get_y();
}
template<typename Lhs, typename Rhs>
Point& operator=(AddOp<Lhs, Rhs> const& op) {
x = op.get_x();
y = op.get_y();
return *this;
}
int get_x() const { return x; }
int get_y() const { return y; }
};
И добавляют соответствующий get_x и get_y в AddOp как функции членства:
int get_x() const {
return lhs.get_x() + rhs.get_x();
}
int get_y() const {
return lhs.get_y() + rhs.get_y();
}
Примечание, как мы не создали временных файлов Точки типа. Это, возможно, была большая матрица со многими полями. Но в то время, когда результат необходим, мы вычисляем его лениво .
У меня нет ничего для добавления к сообщению Konrad, но можно посмотреть Собственный для примера сделанных правильно отложенных вычислений в приложении реального мира. Это - симпатичное воодушевление страха.
C++ 0x хорош и все...., но для тех из нас живущий в подарке у Вас есть библиотека лямбды Повышения и Повышение Финикс. Оба с намерением обеспечения больших объемов функционального программирования к C++.
Что-либо возможно.
Это зависит от точно, что Вы имеете в виду:
class X
{
public: static X& getObjectA()
{
static X instanceA;
return instanceA;
}
};
Здесь у нас есть влияние глобальной переменной, которая лениво оценена при первом использовании.
, Как недавно требуется в вопросе.
И крадущий дизайн Konrad Rudolph и расширение его.
Ленивый объект:
template<typename O,typename T1,typename T2>
struct Lazy
{
Lazy(T1 const& l,T2 const& r)
:lhs(l),rhs(r) {}
typedef typename O::Result Result;
operator Result() const
{
O op;
return op(lhs,rhs);
}
private:
T1 const& lhs;
T2 const& rhs;
};
, Как использовать его:
namespace M
{
class Matrix
{
};
struct MatrixAdd
{
typedef Matrix Result;
Result operator()(Matrix const& lhs,Matrix const& rhs) const
{
Result r;
return r;
}
};
struct MatrixSub
{
typedef Matrix Result;
Result operator()(Matrix const& lhs,Matrix const& rhs) const
{
Result r;
return r;
}
};
template<typename T1,typename T2>
Lazy<MatrixAdd,T1,T2> operator+(T1 const& lhs,T2 const& rhs)
{
return Lazy<MatrixAdd,T1,T2>(lhs,rhs);
}
template<typename T1,typename T2>
Lazy<MatrixSub,T1,T2> operator-(T1 const& lhs,T2 const& rhs)
{
return Lazy<MatrixSub,T1,T2>(lhs,rhs);
}
}
Поскольку это будет сделанным в C++ 0x лямбда-выражениями.