So I came across a problem when working on the OP2Utility code. I am trying to add a Serializer interface to StreamReader/StreamWriter. Unfortunately I'm getting the error code C2385 ambiguous access of 'Serialize'.
The gist of the code is:
This is what the StreamReader series looks like. StreamReader and SeekableStreamReader are meant to be what I would call an abstract class in C#. They cannot exist of their own accord, but are not an interface as they will contain a little bit of data.
class StreamReader : public Serializer {
public:
virtual ~StreamReader() = 0;
// TODO: Include a get function to indicate that it is a StreamReader as opposed to a StreamWriter
};
class SeekableStreamReader : public SeekableSerializer, StreamReader {
};
class FileStreamReader : public SeekableStreamReader {
public:
FileStreamReader(std::string fileName);
~FileStreamReader();
void Serialize(char* buffer, size_t size);
// Change position forward or backword in buffer.
void SeekRelative(int offset);
private:
std::ifstream file;
};
Below are the two Serializer interfaces.
class Serializer {
public:
virtual void Serialize(char* buffer, size_t size) = 0;
};
class SeekableSerializer : public Serializer {
public:
virtual void SeekRelative(int offset) = 0;
};
Anyways, I feel like this would have worked in C#, so something is breaking down in my knowledge of C++. It doesn't seem to like the fact that SeekableStreamReader inherits the function Serialize from both StreamReader and SeekableSerializer. I'm not sure how to set it up otherwise though.
Any help would be appreciated,
Brett
You failed to define Serialize() in SeekableSerializer. Serializer::Serialize() is a pure virtual function and any classes that derive from it must define it. E.g., your code should look like this:
class Serializer {
public:
virtual void Serialize(char* buffer, size_t size) = 0;
};
class SeekableSerializer : public Serializer {
public:
/**
* Pure virtual function, this class cannot be
* instantiated on its own because it's abstract.
*/
virtual void SeekRelative(int offset) = 0;
/**
* Makes this function unambiguous though the above function
* is pure virtual so this is still an abstract class.
*/
virtual void Serialize(char* buffer, size_t size) final
{}
};
As noted above, though, SeekableSerializer is still an abstract class because the function SeekRelative is pure virtual. Though this may be moot as you do define it in FileStreamReader (I think?).
Additionally, SeekableStreamReader and FileStreamReader do not declare virtual destructors. If you're inheriting, your destructors must be virtual in order to be called correctly when a derived object is destroyed.
Finally, StreamReader's destructor is pure virtual. While this is legal C++, you still need to define it. You can do this in the .cpp file simply as this:
StreamReader::~StreamReader() {}
Doesn't need to be any more fancy than that.
Side note about inheriting virtual functions -- even in the derived class you should declare it virtual. This is more of a documentation type of deal than anything else but if you don't want the implementation to be overridden in a further derived class you can define the function final:
class FileStreamReader : public SeekableStreamReader {
public:
FileStreamReader(std::string fileName);
virtual ~FileStreamReader();
virtual void Serialize(char* buffer, size_t size) final;
/** Change position forward or backword in buffer. */
virtual void SeekRelative(int offset) final;
private:
std::ifstream file;
};
Perhaps inheritance isn't the best option. Perhaps class composition would work better. After all, inheritance is not the best option in every case involving classes.
No, in this case inheritance is the correct approach (and actually in this case multiple inheritance makes sense). The intent here is to inherit an interface, not an implementation.
The parent classes provide interface functions but no implementations. Implementations are provided by the derived classes by defining pure virtual functions.