Slicing problem

By default C++ will pass parameters by value. Therefore when you pass an object to a method a copy of this object will be created. Other languages, for example C# will pass parameters by implicit references.

If you work with interfaces and pass parameters by value, the slicing problem can occur. “Working with interfaces” means you normally have a superclass defining functions and a couple of subclasses overwriting these functions.

Within the following example we implement a window base class with a drawing function. To keep it easy we implement a console output as replacement of a complex drawing algorithm. The window base class will be used by a couple of subclasses. The example contains one subclass which of course will overwrite the drawing function and add its own visualization.

class Window
{
public:
  virtual void Draw() const
  {
    std::cout << "window" << std::endl;
  }
};

class ToolWindow : public Window
{
public:
  virtual void Draw() const
  {
    std::cout << "tool window" << std::endl;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  ToolWindow toolWindow = ToolWindow();

  toolWindow.Draw();

  return 0;
}

If we execute the application the output “tool window” is shown.

Now we add a generic drawing function which will get the window interface and call the windows drawing method. So we can use any kind of window subclass and pass it to the generic function.

void Draw(Window window)
{
  window.Draw();
};

int _tmain(int argc, _TCHAR* argv[])
{
  ToolWindow toolWindow = ToolWindow();

  Draw(toolWindow);

  return 0;
}

But now the output is “window”. What’s wrong within this example implementation? We have created a tool window and passed it to the function which expected a window (interface).

If we go back to the start of this article we will find the explanation: “C++ passes parameters by value and therefore creates a copy of the object passed to a method”. As our method expects a window object a new window object was created as copy of the given tool window. But of course the window object does not know the additional implementations of the tool window and therefore this part of the object was “sliced” away. The “slicing problem” occurs when an object of a subclass type is copied to an object of superclass type and thereby losing part of the information which was contained in the subclass.

To implement the functionally we want – a generic function expecting a interface (superclass) – we have to pass the value as reference. This can be done by using one of the most important implementation patterns in C++: “pass by reference to const”. The following source code shows the adapted example. Now we pass the value by using a const reference.

void Draw(const Window& window)
{
  window.Draw();
};

int _tmain(int argc, _TCHAR* argv[])
{
  ToolWindow toolWindow = ToolWindow();

  Draw(toolWindow);

  return 0;
}

This time the output is like expected: “tool window”. The tool window object reference was passed to the function and nothing was spliced away.
Of course, the slicing problem will not only occur for function parameters. Each type cast to a superclass may slice the subclass part away. Therefore I want to finish this article by showing a casting example. The following source code contains two castings: one to a superclass object and one to a superclass reference.

int _tmain(int argc, _TCHAR* argv[])
{
  ToolWindow toolWindow = ToolWindow();

  static_cast<Window>(toolWindow).Draw();
  static_cast<Window*>(&toolWindow)->Draw();

  return 0;
}

Of course we can expect the same slicing problem like in the examples above. The first cast will slice the subclass implementation away and outputs “window” and the second cast outputs “tool window”.

Werbung
Dieser Beitrag wurde unter C++ veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit deinem WordPress.com-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s