2

I have an interface from which a user derives multiple classes which I have no knowledge of but still I want to call these derived classes common method Run().

The Event class is intended to be an interface so I know how to call my unknown UserEvents derived class, since they all must have that Run() method implemented.

I currently have some code and get an error that CallEvent can't allocate an abstract Event. I understand the error, but don't know how I can go about and execute this correctly.

Here's some minimal code example (WandBox):

#include <iostream>

class Event
 {
 public: 
      virtual void Run(int Param) = 0;
 };

 // This is a user event and I have no idea what the class name is,
 // but I still have to call it's method Run() that is common to the interface "Event"
 class UserEvent : public Event
 {
 public:
      virtual void Run(int Param) { std::cout << "Derived Event Dispatched " << Param << std::endl;};
 };

 // This parameter is of pure abstract base class Event because
 // I have no idea what my user class is called.
 void CallEvent(Event WhatEvent)
 {
      WhatEvent.Run(123);
 };

 int main()
 {
      std::cout << "Hello World!" << std::endl;
      UserEvent mE;
      CallEvent(mE);
 }
Pedro
  • 74
  • 1
  • 11
  • 3
    mE is passed by value to CallEvent which accepts a Event. This is called slicing. Change CallEvent to take a pointer or reference. – Richard Critten Jan 31 '18 at 01:53
  • 2
    Change `void CallEvent(Event WhatEvent)` to `void CallEvent(Event& WhatEvent)`. (Which is what Richard Critten said.) – Eljay Jan 31 '18 at 02:01
  • Thanks, it worked! Would it be possible to achieve the same effect if i received a `UserEvent* mE = nullptr` and did something such as `WhatEvent = new Event();` ? [Like so](https://wandbox.org/permlink/2GfmYF9mGXsDyedE) – Pedro Jan 31 '18 at 02:41
  • You cannot `new Event()` as `class Event` is abstract (pure `virtual` `Run()` method). If `class Event` were not abstract what sense would `CallEvent()` make? It gets a pointer `WhatEvent` which it overwrites with a new instance of `Event`. Furthermore: if `WhatEvent` is a pointer the select operator `.` is wrong in `WhatEvent.Run(123)`. Instead, you had to use `WhatEvent->Run(123);`. So, `CallEvent()` should be: `void CallEvent(Event *WhatEvent) { if (WhatEvent) WhatEvent->Run(123); }`. The `if (WhatEvent)` ensures that the call of `Event::Run()` is only done for non-`nullptr`s as it were... – Scheff's Cat Jan 31 '18 at 07:53
  • ...illegal otherwise. – Scheff's Cat Jan 31 '18 at 07:54
  • The usage in `main()` (above) is much better as in the linked sample (on Wandbox). With `CallEvent()` accepting a pointer it should be: ... `UserEvent mE; CallEvent(&mE);`. The address operator `&` provides the pointer to `mE` as it is required by `CallEvent()`. – Scheff's Cat Jan 31 '18 at 07:57
  • While Scheffs approach is valid I'd prefer a reference like Eljay suggested. With `void CallEvent(Event* WhatEvent)` you have to care about what happens when `WhatEvent` is `nullptr` (and yes, maybe you want that). With `void CallEvent(Event& WhatEvent)` it is reasonable to treat `WhatEvent` as a valid instance. – TobiMcNamobi Jan 31 '18 at 08:04

1 Answers1

4

I took your sample code (Like so) and tried to make it running (for illustration):

#include <iostream>

class Event {
  public: 
    virtual void Run(int Param) = 0;
};

// This is a user event and I have no idea what the class name is,
// but I still have to call it's method Run() that is common to the interface "Event"
class UserEvent: public Event {
  public:
    virtual void Run(int Param)
    {
      std::cout << "Derived Event Dispatched " << Param << std::endl;
    }
};

// This parameter is of pure abstract base class Event because
// I have no idea what my user class is called.
void CallEvent(Event *WhatEvent)
{
  std::cout << "in CallEvent(Event *WhatEvent):" << std::endl;
  // Huh? WhatEvent = new Event();
  // wrong: WhatEvent.Run(123);
  // Instead, use ->.
  // For pointers, check for non-nullptr is very reasonable:
  WhatEvent->Run(123);
  // obsolete: delete WhatEvent;
}

// second approach using a reference (as recommended in comments):
void CallEvent(Event &WhatEvent)
{
  std::cout << "in CallEvent(Event &WhatEvent):" << std::endl;
  WhatEvent.Run(123); // for references - select operator . is fine
}

int main()
{
  std::cout << "Hello World!" << std::endl;
  /* nullptr does not make sense:
   * UserEvent *mE = nullptr;
   * Go back to original approach:
   */
  UserEvent mE;
  CallEvent(&mE); // calling the first (with Event*)
  CallEvent(mE); // calling the second (with Event&)
  return 0;
}

Now, it is compilable and runnable. Output:

Hello World!
in CallEvent(Event *WhatEvent):
Derived Event Dispatched 123
in CallEvent(Event &WhatEvent):
Derived Event Dispatched 123

(Life demo on ideone)

I annotated every modification in comments inside the sample code.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • please don't use raw pointers in C++. The reference one is the only variant you should show. – bolov Jan 31 '18 at 09:17
  • 1
    @bolov In general, I would prefer references whenever there are no real reasons to use a pointer. Instead of raw pointers (very dangerous) I prefer smart pointers whenever possible. However, the linked sample code of questioner exposed an attempt to solve with pointer which I wanted to fix. As I wanted to consider references as well, I've shown both solutions. – Scheff's Cat Jan 31 '18 at 09:31