Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: An abstract class as a member

I have a question about style. I have a class (in my case an Option) that depends on the value of an exogenous object (Interest Rate). My goal is to create a abstract base class for the exogenous object (Rate) so that I can construct variations, say SimulatedRate or ConstantRate, that will work inside my depending class, Option.

However, I'm finding in C++, since I obviously cannot instantiate a abstract base class, I must store either a pointer or a reference to the base class. My concern is that when the instantiated exogenous objects go out of scope outside of the dependent class, my dependent class will be pointing to junk.

Is there a reasonable way to utilize polymorphism for this problem in C++?

My current code:

class Dependent 
{
public:
    Dependent(const Exogenous& exo) : exo_(exo) {}
    double getSomething() const { exo_.interfaceMethod(); }
private:
    Exogenous& exo_;
}

class Exogenous
{
public:
    virtual double interfaceMethod() const=0;
}

class ExogenousVariationA
{
public:
    virtual double interfaceMethod() const { return resultA; }
}

class ExogenousVariationB
{
public:
    virtual double interfaceMethod() const { return resultB; }
}
like image 530
curltron Avatar asked Mar 05 '13 00:03

curltron


2 Answers

Your worry is valid. Since you are storing to a reference an object passed in by the client, you are trusting that client to keep the object alive while you need it. This can easily lead to problems. Of course, the same would be true if you used raw pointers to dynamically allocated objects. If the client does delete on the object before you're done with it, once again you have a problem.

The solution is to force the client to give you some kind of responsibility over the lifetime of the object. The way to do this is to ask for a smart pointer. Depending on your problem, you may want a std::unique_ptr or std::shared_ptr. Use the former if you want to take ownership from the client or the latter if you want to share ownership with them. Let's say you choose std::unique_ptr, you would then define your Dependent class as:

class Dependent 
{
public:
    Dependent(std::unique_ptr<Exogenous> exo) : exo_(std::move(exo)) {}
    double getSomething() const { exo_->interfaceMethod(); }
private:
    std::unique_ptr<Exogenous> exo_;
}

The client would use this like so:

std::unique_ptr<Exogenous> ptr(new ExogenousVariationA());
Dependent dep(std::move(ptr));

Now, when your client passes the std::unique_ptr to you, they're giving you ownership of the object. The object will only be destroyed when your std::unique_ptr is destroyed (which will be when your Dependent is destroyed, since it is a member).

Alternatively, if you take a std::shared_ptr then the object will be destroyed once both the client's and your std::shared_ptrs are destroyed.

like image 188
Joseph Mansfield Avatar answered Sep 28 '22 21:09

Joseph Mansfield


sftrabbit has some good advice, to which I'd add:

  • you could create a virtual clone() method in the abstract base class (it's not a virtual base class - that's something else entirely); that method would be implemented in the derived interest rate classes, returning a pointer to a new independent interest rate object that can be owned by the Option; this is particularly useful if the objects contain data that changes as you use it (e.g. from calculations or caching)
  • you probably don't want this, but with std/boost shared pointers it's also possible to ask for a weak pointer to the shared object... that way you can test whether the "owners" of the object (which won't include you) have already finished with it and triggered its destruction

Separately, to use runtime polymorphism your ExogenousVariationA and ~B classes must actually derive from Exogenous, and the method you want to be polymorphically dispatched must be virtual. That looks like this:

class Exogenous
{
public:
    virtual double interfaceMethod() const=0;
}

class ExogenousVariationA : public Exogenous
{
public:
    double interfaceMethod() const { return resultA; }
}
like image 30
Tony Delroy Avatar answered Sep 28 '22 22:09

Tony Delroy