2009-03-25 3 views
4

Я использую 2D-матрицу в одном из моих проектов. Это что-то вроде того, что предлагается в C++ FAQ Lite.Оператор 2D-матрицы и перегрузки()/уродливый синтаксис

Аккуратного то, что вы можете использовать его как это:

int main() 
{ 
    Matrix m(10,10); 
    m(5,8) = 106.15; 
    std::cout << m(5,8); 
    ... 
} 

Теперь у меня есть график, состоящий из вершин и каждая вершина имеет публичный указатель (только для простоты примера) в 2D-матрицу как выше. Теперь у меня есть довольно уродливый синтаксис для доступа к нему.

(*sampleVertex.some2DTable)(0,0) = 0; //bad 
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse... 

Возможно, мне не хватает синтаксического сахара из-за моей неопытности с перегрузкой оператора. Есть ли лучшее решение?

ответ

4
  1. Рассмотрите возможность использования ссылок вместо указателей (при условии, что он не может быть нулевым, и вы можете инициализировать его в конструкторе).
  2. Рассмотрите возможность создания геттера или экземпляра класса оболочки-матрицы для вершины, которая возвращает ссылку на двумерную матрицу (при условии, что она не может быть нулевой).

    sampleVertex.some2DTable()(0,0) = 0; 
    sampleVertex.some2DTableWrap(0,0) = 0; 
    

Однако, мне кажется, что не является проблемой, чтобы оправдать идти через все неприятности.

0

Это самый лучший способ без изменения кода:

//some2DTable is a pointer to a matrix 
(*sampleVertex.some2DTable)(0,0) 

Вы также можете вместо этого сделать some2DTable ссылкой на матрицу вместо указателя на матрицу. Тогда вы бы упростили синтаксис, как в первом sniplet кода.

//some2DTable is a reference to a matrix instead of a pointer to a matrix 
sampleVertex.some2DTable(0,0) 

Или вы могли бы держать some2DTable указатель на ссылку и просто сохранить ссылочную переменную к нему и использовать это в контексте вашего блока кода.

2

Вы в основном ограничены (*sampleVertex.some2DTable)(0,0). Конечно, если вам не нужно перепродавать, почему бы не сохранить фактические значения в матрице?

С другой стороны, сделать указатель приватным и сделать аксессор (примечание: Следующие примеры предполагают матрицу EntryTypes):

Matrix& Vertex::GetTableRef() 
{ 
    return *some2DTable; 
} 
// or 
Matrix::EntryType& Vertex::GetTableEntry(int row, int col) 
{ 
    return (*some2DTable)(row,col); 
} 

// way later... 
myVertex.GetTableRef()(0,0) = 0; 
// or... 
myVertex.GetTableEntry(0,0) = 0; 

Или просто определить инлайн функции, чтобы сделать это для вас, если вы можете» т изменить класс Vertex:

// in some header file 
inline Matrix& GetTableRef(Vertex& v) 
{ 
    return *v.some2DTable; 
} 

// or you could do this 
inline Matrix::EntryType& GetTableEntry(Vertex& v, int row, int col) 
{ 
    return (*v.some2DTable)(row, col); 
} 


// later... 
GetTableRef(myVertex)(0, 0) = 0; 
// or 
GetTableEntry(myVertex, 0, 0) = 0; 

Наконец, не забывайте, что вы не имеют использовать перегрузку операторов. Коллекции STL реализуют функцию at(), которая проверяется, в отличие от оператора [], который не установлен. Если вы не против накладных расходов на проверку границ или если вы просто хотите быть нестандартными, вы можете реализовать at(), а затем просто вызвать myVertex.some2DTable->at(0,0), сохранив немного синтаксическую головную боль вообще.

4

Если у вас есть указатель на матрицу, например. как параметр функции, который вы не можете сделать ссылкой (устаревший код, например.), Вы все равно можете сделать ссылку на него (псевдо-код):

struct Matrix { 
     void operator() (int u, int v) { 
     } 
}; 
int main() { 
     Matrix *m; 
     Matrix &r = *m; 
     r (1,1); 
} 
1

Там нет C++ синтаксический сахар, который облегчит боль, что вы описали:

(*sampleVertex.some2DTable)(0,0) = 0; //bad 
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse... 

В этой ситуации я будет либо иметь граф возвращает ссылку вместо указателя, или матрица определяет функцию, которая вызывает оператор():

inline matrixType &Matrix::get(int x, int y){ return operator()(x,y); } 

Затем синтаксис не совсем так некрасиво для примера вершины:

sampleVertex.some2DTable->get(0,0) = 0; 
1

Я бы добавил функцию, которая возвращает вам ссылку, например, rlbond рекомендует. Для быстрого решения, или если вы не имеете контроля над источником этого, я бы с этим:

sampleVertex.some2DTable[0](0,0) = 0; // more readable 

Это на самом деле эквивалентны, так как имеет место следующее, если a является указателем на определенный класс:

*a == *(a + 0) == a[0] 

См. this долгое обсуждение на comp.lang.C++ об этой же проблеме с хорошими ответами.

+0

heh, fun hack! :) – 2009-03-26 09:24:49

0

я не знаю, стоит ли это проблема, но вы могли бы сделать:

class MatrixAccessor { 
private: 
    Matrix2D* m_Matrix; 
public: 
    MatrixAccessor(Matrix2D* matrix) : m_matrix(matrix) { } 
    double& operator()(int i, int j) const { return (*m_Matrix)(i,j); } 
    Matrix2D* operator->() const { return m_Matrix; } 
    void operator=(Matrix2D* matrix) { m_Matrix = matrix; } 
}; 

При условии, что оригинальный operator() возвращает ссылку (как во многих классах матриц).

Тогда вы предоставите, что MatrixAccessor в классе вершины:

class Vertex { 
    Matrix2D* myMatrix; 

public: 
    MatrixAccessor matrix; 
    Vertex(Matrix2D *theMatrix) : myMatrix(theMatrix), matrix(theMatrix) { } 
}; 

Тогда вы можете написать:

Vertex v; 
v.matrix(1,0) = 13; 
v.matrix->SomeOtherMatrixOperation(); 

EDIT

Я добавил const ключевые слова (благодаря @phresnel для воссоздания темы), чтобы сделать решение s эквивалентно решению, только представляющему публичный Matrix2D -поинтер.

Преимущество этого решения заключается в том, что константность может быть передана объекту матрицы путем добавления двух Непро- const версии operator()() и operator->() (т.е. матрица не может быть изменен на const вершин) и изменение const них, чтобы возвращать const double& и const Matrix2D* соответственно.

Этого было бы невозможно при использовании открытого указателя на объект матрицы.

+0

Я знаю, что это не имеет строгого отношения к ответу, но вы действительно должны удалить myMatrix в Destructor Вершины –

+0

PS: +1 для элегантного решения –

+0

@eJames: Спасибо, вы правы. Вместо этого я удалил выделение матрицы из конструктора. В конце концов, если Vertex отвечает за создание и уничтожение, не имеет смысла использовать указатель ... – MartinStettner

0

Я бы изменил способ захвата «sampleVertex.some2DTable», чтобы он возвращал ссылку.

Либо это, либо создать ссылку самостоятельно:

Matrix& m = *sampleVertex.some2DTable; 
m(1,2) = 3; 
0

Вы могли бы реализовать Матричный оператор :: (Int, Int) путем вызова функции-члена и использовать это один непосредственно при работе с указателями.

class Matrix 
{ 
public: 
    float ElementAt(int i, int j) const { /*implement me*/ } 
    float operator() (int i, int j) const { return ElementAt(i, j); } 
    ... 
}; 

void Foo(const Matix* const p) 
{ 
    float value = p->ElementAt(i, j); 
    ... 
} 

void Bar(const Matrix& m) 
{ 
    float value = m(i,j); 
}