Can I avoid a circular dependency in my Matrix class's iterators?
We have two classes:
template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix
and
template<typename T, typename Size>
class Iterator
Matrix should be able to return begin and end iterators and Iterator will keep a referrence to the Matrix to access the elements via it's interface. We don't want Iterator to depend on the internal storage of the Matrix to prevent coupling. How can we solve this cyclic dep开发者_JAVA技巧endency problem?
(The internal Storage class has the same template parameters as the Matrix class and the same access procedures as the Matrix itself)
First, forward declare the Matrix class. This allows the Iterator class to see the name of the Matrix class, and make pointers and references to it. (It doesn't allow the Iterator class to access member data yet or call member functions yet.)
template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix;
Then, define the Iterator class. All it can do at this point is hold references and pointers to the Matrix. (No access to Matrix's members yet.)
template<typename T, typename Size>
class Iterator{
// don't define any function bodies in here
//but do put all data members and prototypes in here
};
Then define the Matrix class (which can access Iterator members)
template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix{
// don't define any function bodies in here
//but do put all data members and prototypes in here
};
Then define the method bodies for each class. At this point, the methods of both classes can access each other's members. Usually, this part goes in the .cpp file, but for templates it belongs in the .h file.
template<typename T, typename Size, typename Stack, typename Sparse>
Matrix<T,Size,Stack,Sparse>::Matrix(){ /*...*/}
template<typename T, typename Size>
Iterator<T,Size>::Iterator(){ /*...*/ }
Using a nested class might also be appropriate here, and it may cut down on the number of template parameters you need.
template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix{
public:
class Iterator{
// define Iterator members here
};
// declare Matrix members here
}
In order to iterate, iterators typically do need to know about the internal storage they iterate over - this coupling can usually not be avoided. Take a map iterator for example - it is going to have to know about the internal tree structure of the map in order for it to do its job.
You can forward-declare a template. It looks like this:
template<typename T> struct Foo;
template <typename T> struct Bar
{
Foo<T>* foo;
};
template<typename T> struct Foo
{
T value;
Bar<T*> foobar;
};
void bla()
{
Foo<int> grml;
}
Forward-declare the Matrix
template before defining the Iterator
template.
Mind you, you'll hit a brick wall when you realise that an Iterator<T, Size>
can't refer to a Matrix<T, Size, Stack, Sparse>
.
精彩评论