可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm using a 2D matrix in one of my projects. It's something like it is suggested at C++ FAQ Lite.
The neat thing is that you can use it like this:
int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout << m(5,8);
...
}
Now, I have a graph composed of vertices and each vertex has a public (just for simplicity of the example) pointer to 2D matrix like above. Now I do have a pretty ugly syntax to access it.
(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
Probably I'm missing some syntactic sugar here due to my inexperience with operator overloading. Is there a better solution?
回答1:
- Consider using references instead of pointers (provided, it can't be null and you can initialize in the constructor).
Consider making a getter or an instance of a matrix wrapper class for a vertex that returns a reference to 2D matrix (provided, it can't be null).
sampleVertex.some2DTable()(0,0) = 0;
sampleVertex.some2DTableWrap(0,0) = 0;
However, to me it sounds like a non-issue to justify going through all the trouble.
回答2:
If you have a pointer to a Matrix, e.g. as a function parameter that you can't make a reference (legacy code, e.g.), you can still make a reference to it (pseudo code):
struct Matrix {
void operator () (int u, int v) {
}
};
int main () {
Matrix *m;
Matrix &r = *m;
r (1,1);
}
回答3:
You're basically limited to (*sampleVertex.some2DTable)(0,0)
. Of course, if you don't need reseating, why not store the actual values in the matrix instead?
Alternatively, make the pointer private and make an accessor (note: the following examples assume a matrix of 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;
Or, just define an inline function to do this for you if you can't change the class 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;
Finally, don't forget that you don't have to use operator overloading. STL collections implement an at() member function, which is checked, as opposed to operator[] which is unchecked. If you don't mind the overhead of bounds checking, or if you just want to be nonstandard, you could implement at() and then just call myVertex.some2DTable->at(0,0)
, saving a bit of a syntactic headache altogether.
回答4:
There is no C++ syntactic sugar that will ease the pain of what you describe:
(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
In this situation, I would either have the graph return a reference instead of a pointer, or have the matrix define a function which calls the operator():
inline matrixType &Matrix::get( int x, int y ){ return operator()(x,y); }
Then, the syntax isn't quite as ugly for the vertex example:
sampleVertex.some2DTable->get(0,0) = 0;
回答5:
I would add a function that returns you a ref like rlbond recommends. For a quick fix or if you don't have control over the source of it, i would go with this:
sampleVertex.some2DTable[0](0,0) = 0; // more readable
That's actually equivalent, because the following holds if a
is a pointer to a defined class:
*a == *(a + 0) == a[0]
See this long discussion on comp.lang.c++ about that same problem with good answers.
回答6:
This is the best way without changing your code:
//some2DTable is a pointer to a matrix
(*sampleVertex.some2DTable)(0,0)
You could also instead make some2DTable a reference to a matrix instead of a pointer to a matrix. Then you would have simplified syntax as in your first code sniplet.
//some2DTable is a reference to a matrix instead of a pointer to a matrix
sampleVertex.some2DTable(0,0)
Or you could keep some2DTable a pointer to a reference and simply store a reference variable to it and use that in the context of your code block.
回答7:
I'd change the way you get hold of "sampleVertex.some2DTable" so it returns a reference.
Either that or create the reference yourself:
Matrix& m = *sampleVertex.some2DTable;
m(1,2) = 3;
回答8:
I don't know if it's worth the trouble, but you could do:
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; }
};
Provided the original operator()
returns a reference (as it is in many matrix classes).
Then you provide that MatrixAccessor in your vertex class:
class Vertex {
Matrix2D* myMatrix;
public:
MatrixAccessor matrix;
Vertex(Matrix2D *theMatrix) : myMatrix(theMatrix), matrix(theMatrix) { }
};
Then you can write:
Vertex v;
v.matrix(1,0) = 13;
v.matrix->SomeOtherMatrixOperation();
EDIT
I added const
keywords (thanks to @phresnel for bringing up the topic) in order to make the solution semantically equivalent to a solution only presenting a public Matrix2D
-pointer.
An advantage of this solution is that constness could be transferred to the matrix object by adding two non-const
versions of the operator()()
and operator->()
(i.e. the matrix cannot be modified on const
vertices) and changing the const
ones to return a const double&
and const Matrix2D*
respectively.
That would not be possible when using a public pointer to the matrix object.
回答9:
You could implement Matrix::operator (int,int) by calling a member function and use that one directly when dealing with pointers.
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);
}