MindSeeker
MindSeeker

Reputation: 600

C++ Changing owned object type in child class

We have four classes: A, B, X, and Y. B inherits from A and Y inherits from X. Each instance of A creates an owned object of type X. Some Pseudocode:

class A {
    protected:
        X * OwnedObject;
}

class B : public A {
    B();
    void BFunction();
};

B::B() {
    OwnedObject = new Y();
}

B::BFunction() {
    OwnedObject -> YFunction(); //error, class A has no member named 'YFunction()'
}

class X {};

class Y : public X {
    void YFunction(); //this function is new, it is not in X
};

B, of course, inherits the pointer to X. What if we want B to create (and its OwnedObject pointer to point to) an object of child class Y instead of parent class X? What is the most correct way to approach this problem?

Edit: Sorry if I was unclear, I added some more Pseudocode to illustrate more specifically the problem I'm having and what I want to do. Thanks for the input so far!

Upvotes: 3

Views: 153

Answers (3)

adrin
adrin

Reputation: 4886

I can think of two different approaches.

In the first one, I'd assume that class X can know about the functions that its children might change/implement, but it (X) itself doesn't know the exact implementation. In this case, I'd have a virtual function in X, and depending on if there is the possibility of instantiating from X or not, have two implementations:

// this X can not be instantiated
class X{
    public:
        virtual void f() = 0;
}
class Y : public X{
    public:
        virtual void f() { stuff to do; }
}

Or:

// this X can be instantiated
class X{
    public:
        virtual void f() { };
}
class Y : public X{
    public:
        virtual void f() { stuff to do; }
}

Or in another scenario, I'd assume that because B is instantiating from Y, then it knows that it's instantiating from Y, therefore it can cast it safely to Y, whenever it wants to use it:

B::BFunction() {
    static_cast<Y*>(OwnedObject) -> YFunction(); 
}

Or maybe if you want to be on the safe side:

B::BFunction() {
    if (dynamic_cast<Y*>(OwnedObject) != null)
        static_cast<Y*>(OwnedObject) -> YFunction();
}

Upvotes: 1

Sebastian
Sebastian

Reputation: 1889

The static_cast solution of adrin is probably the simplest thing you can do. You just need to be sure that OwndedObject always is an instance of Y (and not X or another subclass of it).

You can check that at runtime (see dynamic_cast in answer of adrin, but use result of dynamic_cast instead of adding another static_cast).

B::BFunction() {
    if (Y* y = dynamic_cast<Y*>(OwnedObject))
        y->YFunction();
}

Or you can declare the pointer const (the pointer, not the pointee! "const" keyword after the star), and initialize it in the constructor (answer of ichramm). Then, you only need to look at the constructor of A and B in order to reason that your static_cast is safe.

You could configure the type of the OwnedObject by a template. Inspired by this: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern.

Or, instead of storing the pointer in your base class, make a virtual member function which provides read access to it:

class A_interface {
public:
    virtual X* getX() = 0;
};
class A: public A_interface {
    X* OwnedObject;
public:
    virtual X* getX() {return OwnedObject;}
};
// B is no subclass of A anymore, because you don't want that X* member.
class B: public A_interface {
    Y* OwnedObject;
public:
    virtual X* getX() {return OwnedObject;}
};

By desing, you can't have a setX(X*) virtual member function in A_interface, because you couldn't implement that in B. http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

Upvotes: 0

ichramm
ichramm

Reputation: 6642

Someting like this?

class A {
protected:
    A(X *x) : OwnedObject(x) {
        // code...
    }

    X * OwnedObject;
}

class B : public A {
public:
    B() : A(new Y()) {
        // code...
    }
};

I just added to A the possibility to inject a X object from any child class

Upvotes: 0

Related Questions