What am I not getting about this abstract class implementation?
PREFACE: I'm relatively inexperienced in C++ so this very well could be a Day 1 n00b question.
I'm working on something whose long term goal is to be portable across multiple operating systems. I have the following files:
Utilities.h
#include <string>
class Utilities
{
public:
Utilities() { };
virtual ~Utilities() { };
virtual std::string ParseString(std::string const& RawString) = 0;
};
UtilitiesWin.h (for the Windows class/implementation)
#include <string>
#include "Utilities.h"
class UtilitiesWin : public Utilities
{
public:
UtilitiesWin() { };
virtual ~UtilitiesWin() { };
virtual std::string ParseString(std::string const& RawString);
};
UtilitiesWin.cpp
#include <string>
#include "UtilitiesWin.h"
std::string UtilitiesWin::ParseString(std::string const& RawString)
{
// Magic happens here!
// I'll put in a line of code to make it seem valid
return "";
}
So then elsewhere in my code I have this
#include <string>
#include "Utilities.h"
void SomeProgram::SomeMethod()
{
Utilities *u = new Utilities();
StringData = u->ParseString(StringData); // StringData defined elsewhere
}
The compiler (Visual Studio 2008) is dying on the instance declaration
c:\somepath\somecode.cpp(3) : error C2259: 'Utilities' : cannot instantiate abstract class
due to following members:
'std::string Utilities::ParseString(const std::string &)' : is abstract
c:\somepath\utilities.h(9) : see declaration of 'Utilities::ParseString'
So in t开发者_运维问答his case what I'm wanting to do is use the abstract class (Utilities) like an interface and have it know to go to the implemented version (UtilitiesWin).
Obviously I'm doing something wrong but I'm not sure what. It occurs to me as I'm writing this that there's probably a crucial connection between the UtilitiesWin implementation of the Utilities abstract class that I've missed, but I'm not sure where. I mean, the following works
#include <string>
#include "UtilitiesWin.h"
void SomeProgram::SomeMethod()
{
Utilities *u = new UtilitiesWin();
StringData = u->ParseString(StringData); // StringData defined elsewhere
}
but it means I'd have to conditionally go through the different versions later (i.e., UtilitiesMac()
, UtilitiesLinux()
, etc.)
What have I missed here?
Utilities *u = new Utilities();
tells the compiler to make a new instance of the Utilities
class; the fact that UtilitiesWin
extends it isn't necessarily known and doesn't affect it. There could be lots of classes extending Utilities
, but you told the compiler to make a new instance of Utilities
, not those subclasses.
It sounds like you want to use the factory pattern, which is to make a static method in Utilities
that returns a Utilities*
that points to a particular instance:
static Utilities* Utilities::make(void) {return new UtilitiesWin();}
At some point you're going to have to instantiate a non-abstract subclass; there's no way around specifying UtilitiesWin
at that point
You seem a bit confused as to what you want; you have to tell the computer at some stage which implementation of Utilities it is to use, but with the shape you've set out you only need to have
#ifdef windows
Utilities* u = new UtilitiesWin();
#endif
#ifdef spaceos3
Utilities* u = new UtilitiesSpaceOS3();
#endif
once in the program, and most of the source files can just call methods of u without knowing what kind of a u it is - which is I think what you were aiming at.
In C++ you cannot instantiate abstract classes, which is precisely what you are trying to do here:
Utilities *u = new Utilities();
It's very unclear to me why you would want to instantiate such a class, and what you would do with it if you could do so (which you can't). You cannot use an instantiation as an interface - the class definition provides that.
You are "getting" it right, you have to instantiate a concrete type. There are common solutions to this.
Yes, you have to make that decision which class to instantiate somewhere.
The implementation of that depends on the criteria for this decision: is it fixed for the binary? The same choice for each process? Or does it change for every instance of SomeProgram
?
Fore the concrete classes you mention, the decision can probably be made at compile time, similar to what Tom suggests.
Second, SomeProgram
should not make this choice itself. Rather the type or the instance should be configurable from the outside. The most simple approach is to pass the concrete instance to the constructor of SomeProgram:
class SomeProgram
{
private:
Utilities * m_utilities;
public:
Someprogram(Utilities * util) : m_utilities(util) {}
}
Note that SomeProgram
only "knows" the abstract class, none of the concrete classes.
For delayed construction, use a factory. If the utilities class should be injected as above, is expensive to create but isn't necessary most of the time, you would inject a factory instead: you pass a UtilityFactory
to the class, which SomeProgram can use to create the required instance on demand. The actual factory implementation decides the concrete class to chose. See Factory pattern for more.
If that's a common problem, look at Inversion of Control (IoC) - there are several library implementations out there that make that easier. It has become a buzzword in the wake of agressive unit testing, where replacing "real" implementations with mocks has to happen permanently. (I'm still waiting for a complete MockOS, though). I haven't worked on any application that seriously needed suhc a library in practice, though, and it is very likely overkill for your problem.
精彩评论