views:

104

answers:

4

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 this 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?

+7  A: 
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

Michael Mrozek
This looks like the connection I just wasn't making. Thanks.
Schnapple
+2  A: 

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.

Tom Womack
+2  A: 

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.

anon
Yeah I was misunderstanding abstract classes and using the instantiation as an interface. Thanks.
Schnapple
+2  A: 

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 SomeProgramonly "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 UtilityFactoryto 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.

peterchen