0

The goal is to have an object which can change its behaivor.
My object is wrapper which should call One::handler() and output "One" but instead it output "from virtual handler" from Situation::handler().
I didn't write the change method because I'm stucking here.

#include <iostream>

class Situation
{
public:
    virtual void handler()
    {
        std::cout<<"from virtual handler()";
    }
};

class Wrap
{
private:
    Situation _sit;
public:
    Wrap(Situation sit)
    {
        _sit = sit;
    }
    void call()
    {
        _sit.handler();
    }

};

class One : public Situation
{
public:
    void handler()
    {
        std::cout<<"One"<<std::endl;
    }
};

int main()
{
    One first;
    Wrap wrapper(first);
    wrapper.call();
    return 0;
}
Module_art
  • 999
  • 2
  • 9
  • 26
  • 1
    Possible duplicate of [What is object slicing?](https://stackoverflow.com/questions/274626/what-is-object-slicing) – user0042 Dec 30 '17 at 10:23
  • As is often the case in C++, solving this problem will give you one or more new problems. You will have to consider whether the wrapper to "own" the wrapped object, or simply reference it. And if it wraps it, should it have value semantics (i.e. what should be the effect of copying `Wrap` objects?) – juanchopanza Dec 30 '17 at 10:25
  • "solving this problem will give you one or more new problems" yeah welcome in my world – Module_art Dec 30 '17 at 10:30

4 Answers4

3

You cannot wrap a polymorphic object that you pass by value because of object slicing. Your Wrapper needs to have a pointer or a reference to the object that it wraps.

Here is an implementation that uses a reference:

class Wrap
{
private:
    Situation& _sit;
public:
    Wrap(Situation& sit) : _sit(sit)
    {
    }
    void call()
    {
        _sit.handler();
    }
};

Note that this implementation is nearly identical to your original one, with & added in two places, and initializer list used to set _sit reference. However, it is fragile, because you must ensure that the lifetime of the object referenced by _sit stretches beyond the last point where its Wrapper is used.

A more robust implementation of the wrapper would use smart pointers to dynamically allocated objects.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

Since your wrapper takes (and stores) its Situation object by copy the derived type information is lost, all that will remain is the base class, this is called slicing.

If you want to keep the derived parts pass a pointer or reference to the wrapper constructor and change the member type to some pointer type (possible unique_ptr if the wrapper is to take wonership of the derived type instance).

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
0

You pass first via the sit parameter by value. It means that sit is a copy of first of the type Situation as you declared, not the originated one of the type One. By the way you copied only small part of the class One.

To call a virtual method you should pass your object one by reference or pointer.

273K
  • 29,503
  • 10
  • 41
  • 64
0

Your problem is passing an object of the base class by value rather than a reference or pointer – you basically cast your One back to a Situation (also stripping extra data) and therefore get your result.

In fact, since you're using polymorphism (virtual members and inheritance), you don't even need your wrapper to begin with:

class Situation {
public:
    virtual void handle() {
        std::cout << "Base Situation" << std::endl;
    }
}

class One : public Situation {
public:
    virtual void handle() {
        std::cout << "One Situation" << std::endl;
    }
}

int main(int argc, char **argv) {
    // These pointers point to any base class object or derived object
    Situation *first = new Situation();
    Situation *second = new One();

    // Calling a virtual member will automatically call the most derived class's member
    first->handle(); // prints "Base Situation"
    second->handle(); // prints "One Situation"

    delete first;
    delete second;
}
Mario
  • 35,726
  • 5
  • 62
  • 78
  • I don't think you can say that OP doesn't need a wrapper. It sounds like the whole point of the question. – juanchopanza Dec 30 '17 at 10:30
  • @juanchopanza Yes, and to be honest, that's the part confusing me. It isolates the implementation, but due to the common base class it feels kind of unnecessary. You're basically abstracting it twice, if you implement both a wrapper and the base class/interface. – Mario Dec 30 '17 at 14:04