Home > database >  object constructed as one base class, method calculated as other base class (diamond inheritance and
object constructed as one base class, method calculated as other base class (diamond inheritance and

Time:06-02

Update: I eliminated all references to shapes, since that was not the point of the question.


I have a diamond inheritance scheme in C solved through virtual inheritance.

The base class is general has two attributes and some method using them.

class general {
    double attr1, attr2;
public:
    shape(double attr1, double attr2) {
        this->attr1=attr1;
        this->attr2=attr2;
    }
    virtual double somemethod() {
        return attr1*attr2;
    }
};

A specialrestriction is a general with a special restriction that both attributes are equal.

class specialrestriction : public general {
public:
    specialrestriction(double attr) : general(attr, attr) {};
};

A specialrule slightly modifieds somemethod() with a special rule.

class specialrule : public general {
public:
    specialrule(double a, double b) : general(2*a, 2*b) {}
    double somemethod() override {
        return general::somemethod()*0.785398;
    }
};

Finally a restrictionandrule is a general with the same restriction as a specialrestriction (I would like to use its constructor), and the same special rule for computing the somemethod() as a specialrule (I would like to use its somemethod() method). The solution I have so far is:

class restrictionandrule : public virtual specialrule, public virtual specialrestriction {
public:
    restrictionandrule(double attr) : specialrestriction(attr), specialrule(attr,attr) {} //the call to the constructor of specialrule is (almost?) redundant
    double somemethod() override { return specialrule::somemethod(); } //only specialrule overrides somemethod(), is there another way to 
};

int main() {
    restrictionandrule c(5);
    std::cout<<"restrictionandrule::somemethod is " << c.somemethod()<<std::endl;
    return 0;
}

As put in the code's comments, I think the code of restrictionandrule is redundant. My intention when doing this was avoiding as much code redundancy as possible, but couldn't go any further.

(As a side comment, I'd like the language to solve the somemethod() ambiguity with less code (e.g., by calling the first parent's method by default), but to my knowledge that's just the standard, and this is not my question.)

My question is this: Since both specialrule and specialrestriction do little more (from the programmer's POV, I know I'm oversimplifying!) than calling the base constructor, I don't really need both of them, but C enforces calling both. Is there a better way to do this? Do I have this problem because I'm misunderstanding the `an A is a B' relationship? (perhaps because derived classes are not supposed to have less attributes than the base?)

CodePudding user response:

This is how I would implement the hierarchy

#define _USE_MATH_DEFINES         //for older compilers
#include <cmath>

class shape {
public:
    virtual ~shape() {}
    virtual double area() = 0;
};

class rectangle : public shape {
protected:
    double mBase;
    double mHeight;

public:
    rectangle(double base, double height) : mBase(base), mHeight(height) {}
    virtual ~rectangle() {}
    virtual double area() override { return mBase*mHeight; }
};

class square : public rectangle {
public:
    square(double side) : rectangle(side, side) {};
    virtual ~square() {}
};


class ellipse : public shape {
protected:
    double mMajor;
    double mMinor;

public:
    ellipse(double major, double minor) : mMajor(major), mMinor(minor) {}
    ~ellipse() {}
    virtual double area() override { return mMajor * mMinor * M_PI; }
};

class circle : public ellipse {
public:
    circle(double radius) : ellipse(radius,radius) {}
    virtual ~circle() {}
};

// --------------------------------------------------------------
#include <iostream>
#include <memory>

int main() {            
    std::unique_ptr<shape> widget = std::make_unique<circle>(2.0);
    std::cout << "Area is: " << widget->area() << "\n";
}

Demo: https://godbolt.org/z/4bTscEEvT

Next step would be to template the types.

You could use a std::pair instead of having mBase, mHeight etc, but then you would be sacrificing readability for a dubious level of avoiding duplication.

CodePudding user response:

Just because you aren't actually using specialrestriction doesn't mean you can just leave it uninitialized. For every object of a class type, its constructor will be called upon construction (and its destructor upon destruction). In general the compiler can't know that the body of specialrestriction's constructor is empty (for instance, if it was defined in a different compilation unit), so failing to call it could easily violate specialrestriction's assumptions about its internal state.


Note that you haven't correctly implemented virtual inheritance. Your hierarchy still has two instances of general. An object of type restrictionandrule looks like this:

┌─────────────────────────┐
│restrictionandrule       │
│ ┌─────────────────────┐ │
│ │specialrule          │ │
│ │ ┌─────────────────┐ │ │
│ │ │general          │ │ │
│ │ │                 │ │ │
│ │ │                 │ │ │
│ │ │                 │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────┘ │
│                         │
│ ┌─────────────────────┐ │
│ │specialrestriction   │ │
│ │ ┌─────────────────┐ │ │
│ │ │general          │ │ │
│ │ │                 │ │ │
│ │ │                 │ │ │
│ │ │                 │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────┘ │
└─────────────────────────┘

Each of the specialrule and specialrestriction syb-objects has its own general sub-object, and so you need a separate constructor call to initialize each of them separately. In fact, in your specific example your two general sub-objects have different values for attr1 and attr2. This is also why you need to explicitly override somemethod in restrictionandrule. Without your help the compiler doesn't know which direct parent you want to call it on.

To eliminate the extra general sub-object, specialrule and specialrestriction need to inherit virtually from general (restrictionandrule does not need to inherit virtually from specialrule and specialrestriction though).

  • Related