Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delegate to implementing class

Tags:

c++

I have an abstract base class Node, which is derived from an abstract Interface class IObservable. There is a several classes implementing the abstract IObservable: SingleObservable and MultiObservable

I want to create a class ObservableNode, derived from the base Node class and specify on its declaration which class to use for the implementation of the IObservable interface.

I've added using ... statements for every pure virtual method in IObservable, referring to the methods in the implementation classes but I still get errors saying that ObservableNode is an abstract class, missing the implementation of notifyObservers(IObject*).

If I add the parameter IObject* to the using statement I get an "expected ';' before '(' token" error

How can I solve this?

class IObservable {
  public:
    virtual ~IObservable() {};
    virtual void notifyObservers(IObject*) = 0;
};
class SingleObservable: public IObservable {
  public:
    virtual ~SingleObservable() {};
    void notifyObservers(IObject*) override { 
      //some implementaiton
    };
};
class MultiObservable: public IObservable {
  public:
    virtual ~MultiObservable() {};
    void notifyObservers(IObject*) override { 
      //some other implementaiton
    };
};

class Node: public IObservable {
  public:
    virtual ~Node() {};
};

class ObservableNode: public Node, public SingleObservable {
  public:
    virtual ~ObservableNode() {};
    using SingleObservable::notifyObservers;
    // using SingleObservable::notifyObservers(IObject*); // expected ';' before '(' token error

};


Node* node = new ObservableNode() // instantiating abstract class error, missing notifyObservers(IObject*) implementation
like image 444
Bascy Avatar asked Apr 28 '19 12:04

Bascy


People also ask

How do you implement a class?

The implements keyword is used to implement an interface . The interface keyword is used to declare a special type of class that only contains abstract methods. To access the interface methods, the interface must be "implemented" (kinda like inherited) by another class with the implements keyword (instead of extends ).

What are class delegates?

A delegate is a reference type variable that holds the reference to a method. The reference can be changed at runtime. Delegates are especially used for implementing events and the call-back methods. All delegates are implicitly derived from the System. Delegate class.

What is the delegate method?

A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.

What does it mean to delegate a task?

Delegation refers to the transfer of responsibility for specific tasks from one person to another. From a management perspective, delegation occurs when a manager assigns specific tasks to their employees.


2 Answers

Your problem seems to be that you inherit Node which is still abstract, and also causes to introduce the good old multimple inheritance vicious diamond problem. When I change your code like this, the error disappears:

class Node: public IObservable {
  public:
    virtual ~Node() {};
    // ** Added an implementation here **
    void notifyObservers(IObject*) override { 
          //some other implementaiton
    };
};

class ObservableNode: public virtual Node, public virtual SingleObservable {
                          // ^^^^^^^              ^^^^^^^
  public:
    virtual ~ObservableNode() {};
    using SingleObservable::notifyObservers;
};

int main() {
    Node* node = new ObservableNode();
}

See it live on coliru.

like image 175
πάντα ῥεῖ Avatar answered Sep 28 '22 15:09

πάντα ῥεῖ


@πάντα ῥεῖ's answer describe one workaround, but possible this is not what OP is after here. Also, as my comment describe under the answer, the approach in the answer might give unexpected results e.g. when invoking node->notifyObservers(obj):

Note that in this particular example, Node* node = new ObservableNode(); will mean node->notifyObservers(obj) will invoke Node::notifyObservers(IObject*) and not SingleObservable::notifyObservers(IObject*), which might be unexpected, considering we instantiate an ObservableNode object which specifies using SingleObservable::notifyObservers;.

In OP's original code, we are suffering from multiple inheritance ambiguity, as we are not using virtual inheritance when Node and SingleObservable (and MultiObservable) derives from IObservable:

class SingleObservable: public IObservable {
  public:
    virtual ~SingleObservable() {};
    void notifyObservers(IObject*) override { 
      //some implementaiton
    };
};

class Node: public IObservable {
  public:
    virtual ~Node() {};
};

Meaning our the object's memory layout, w.r.t. inheritance, of ObservableNode to looks like the following

 IObservable  IObservable
           |  |
        Node  SingleObservable
           \  /
       ObservableNode

whereas, in this context, we are likely to want an object's memory layout looking as follows

       IObservable
           /  \
        Node  SingleObservable
           \  /
       ObservableNode

If we were to correct this, Node can stay abstract, and a call to node->notifyObservers(obj) with node as OP's example will result in invocation of SingleObservable::notifyObservers, as might have been expected.

class Node: public virtual IObservable {
                // ↑↑↑↑↑↑↑
  public:
    virtual ~Node() {};
};

class SingleObservable: public virtual IObservable {
                            // ↑↑↑↑↑↑↑
  public:
    virtual ~SingleObservable() {};
    void notifyObservers(IObject*) override { 
        std::cout << "SingleObservable::notifyObservers";
    };
};

struct DummyObj : public IObject {};

int main() {
    Node* node = new ObservableNode();
    DummyObj obj;
    node->notifyObservers(obj);  // SingleObservable::notifyObservers
}

Note that we not need virtual inheritance for when ObservableNode derives from Node and SingleObservable.

Finally, if we'd want Node be non-abstract (specifically, to provide an override of void notifyObservers(IObject*)), then ObservableNode must provide it's own (final) override of it, as we will otherwise inherit two final overrides of it in ObservableNode (one from Node and one from SingleObservable). In this case, ObservableNode could simply define its own override which explicitly calls the base class of choice, e.g.

class Node: public virtual IObservable {
  public:
    virtual ~Node() {};
    void notifyObservers(IObject*) override { 
        std::cout << "Node::notifyObservers";
    };
};

class SingleObservable: public virtual IObservable {
  public:
    virtual ~SingleObservable() {};
    void notifyObservers(IObject*) override { 
        std::cout << "SingleObservable::notifyObservers";
    };
};

class ObservableNode: public Node, public SingleObservable {
  public:
    virtual ~ObservableNode() {};
    // Non-ambiguous final override in ObservableNode.
    // We could use `override` specifier here, but we might as well
    // use `final`, if we are not expecting something to derive from ObservableNode.
    void notifyObservers(IObject* obj) final { 
        SingleObservable::notifyObservers(obj);
    };
};

struct DummyObj : public IObject {};

int main() {
    Node* node = new ObservableNode();
    DummyObj obj;
    node->notifyObservers(obj);  // SingleObservable::notifyObservers
}

See ISO C++ FAQ - Inheritance — Multiple and Virtual Inheritance for details on the diamond inheritance structure and virtual inheritance.

like image 26
dfrib Avatar answered Sep 28 '22 15:09

dfrib