简体   繁体   中英

In C++ Is there a way for the Child to reuse the the Parent class implementation of the pure virtual function defined in GrandParent

Consider the below code, EventGeneratorBase is a helper class intended to provide the actual implementation for AddEventHandler() and I would like to use that implementation in the class RemoteControl instead of explicity defining it. I know it's not possible to instantiate RemoteControl without defining the method but is there a shortcut or an easy way to avoid manually defining the methods.


Note: The code in it's present form doesn't compile because RemoteControl can't be instantiated.

#include <iostream>
#include <vector>
#include <memory>

template<class TEventHandler> struct IEventGenerator {
    virtual ~IEventGenerator() = default;
    virtual void AddEventHandler(std::weak_ptr<TEventHandler> eventHandler) = 0;
};

template <class TEvents> struct EventGeneratorBase : IEventGenerator<TEvents> {
    void AddEventHandler(std::weak_ptr<TEvents> target) {
        _eventHandlers.push_back(target);
    }

    std::vector<std::weak_ptr<TEvents>> GetEventHandlers() {
        return _eventHandlers;
    }

private:
    std::vector<std::weak_ptr<TEvents>> _eventHandlers;
};


struct IControlEvents {
    virtual ~IControlEvents() = default;
    virtual void PowerOn() = 0;
    virtual void PowerOff() = 0;
};

struct IRemoteControl  : IEventGenerator<IControlEvents> {
    virtual ~IRemoteControl() = default;
    virtual void Toggle() = 0;
};

struct RemoteControl : IRemoteControl, EventGeneratorBase<IControlEvents> {
    // I don't want to define AddEventHandler() in this class and
    // would like to inherit the implementation from EventGeneratorBase

    void Toggle() {
        for (auto tref : GetEventHandlers()) {
            auto t = tref.lock();
            if (t) {
                t->PowerOn();
                t->PowerOff();
            }
        }
    }
};

struct Light : IControlEvents {
    Light(std::string color) : _color(color) { }

    void PowerOn() {
        std::cout << _color << "::Light ON!" << std::endl;
    }

    void PowerOff() {
        std::cout << _color << "::Light OFF!" << std::endl;
    }

private:
    std::string _color;
};

int main() {
    std::shared_ptr<IRemoteControl> remote(new RemoteControl); // ERROR: Can't instantiate
    std::shared_ptr<IControlEvents> light1(new Light("GREEN"));
    std::shared_ptr<IControlEvents> light2(new Light("RED"));

    remote->AddEventHandler(light1);
    remote->AddEventHandler(light2);
    remote->Toggle();

    return 0;
}

Your problem is that you have two distinct sub-objects of type IEventGenerator<IControlEvents> within your RemoteControl object. One via EventGeneratorBase<IControlEvents> and one via IRemoteControl .

There are two ways to prevent you from having two distinct subobjects. The first is to inherit virtual ly from IEventGenerator<TEventHandler> in both spots. This has a modest run-time cost. Simply add virtual before every case of inheritance from IEventGenerator<?> and you are done.

A second method is to note that EventGeneratorBase is intended to help with implementing IEventGenerator .

template<class T> struct tag{using type=T;};
template<class Tag> using type_t=typename Tag::type;

template<class TEventHandler>
tag<TEventHandler> get_event_handler_type(
  IEventGenerator<TEventHandler> const*
) { return {}; }
template<class X>
using event_handler_type = type_t< decltype( get_event_handler_type( (X*)nullptr ) ) >;

template <class Base, class TEvents = event_handler_type<Base>>
struct EventGeneratorHelper :
  Base
{
  void AddEventHandler(std::weak_ptr<TEvents> target) override {
    _eventHandlers.push_back(target);
  }

  std::vector<std::weak_ptr<TEvents>> GetEventHandlers() {
    return _eventHandlers;
  }

private:
  std::vector<std::weak_ptr<TEvents>> _eventHandlers;
};

now, go down to here:

struct RemoteControl :
  EventGeneratorHelper<IRemoteControl>
{

and change how we inherit. We now interpose EventGeneratorHelper between us and IRemoteControl , so they now share the same common IEventGenerator .

This removes the need for virtual inheritance, but does up your compile time, and can cause some executable code bloat.

We can go a step further. Add this to EventGeneratorHelper :

template<class Action>
void FireEvents( Action&& action ) const {
  for (auto tref : GetEventHandlers()) {
    auto t = tref.lock();
    if (t) {
      action(t);
    }
  }
}

which reduces RemoteControl to:

struct RemoteControl :
  EventGeneratorHelper<IRemoteControl>
{
  void Toggle() {
    this->FireEvents([](std::shared_ptr<IRemoteControl> const& ptr){
      t->PowerOn();
      t->PowerOff();
    });
  }
};

which I think is nice -- requiring clients to know the right way of iterating seems silly.

You have a problem in your inheritance hierarchy.

template <class TEvents> struct EventGeneratorBase :IEventGenerator<TEvents> {
    [...]
};

struct IRemoteControl  : IEventGenerator<IControlEvents> { 
    [...]
};

struct RemoteControl : IRemoteControl, EventGeneratorBase<IControlEvents> {
    [...]
};

This is not doing what you might expect. Instead, your class RemoteControl inherits twice from IEventGenerator , once from IRemoteControl and once from EventGeneratorBase .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM