1

I have several classes derived from a top-level superclass:

base: CDeviceClientRequest sub-class: CDeviceSetEnabledRequest (base) inst-class: CDeviceGPSSetEnabledRequest* (sub-class) inst-class: CDeviceTotalStationSetEnabledRequest* (sub-class)

Only those marked with a * are instantiable, the others have protected constructors, i.e. the constructors invoke the base class constructors, setting the private member variables.

I am constructing them via a method:

CDeviceClientRequest getRequest(int type)
{
    switch (type)
    {
    GPS_SET_ENABLED: return CDeviceGPSSetEnabledRequest();
    TS_SET_ENABLED : return CDeviceTotalStationSetEnabledRequest();
    default:
       // raise an unknown type exception
    }
}

I then have some code that invokes the request:

void invoke(CDeviceClientRequest& request)
{
    // some code
}

So I have code somewhere that looks like this:

CDeviceClientRequest request = getRequest(GPS_SET_ENABLED);
invoke(request);

My problem is that although I am invoking the constructor for CDeviceGPSSetEnabledRequest(), what gets returned from getRequest(), is a CDeviceClientRequest, and what is passed to invoke is also a CDeviceClientRequest, and not a CDeviceGPSSetEnabledRequest, like I expected.

I verified this by adding a simple whatAmI() method to all super and base classes to std::cout the name of the class, and I only ever get "I am a CDeviceClientRequest". Somewhere the fact that it is dealing with a derived class has been lost.

Any help greatly appreciated. (Note: I have greatly simplified the code for the post).

Marcus MacWilliam
  • 602
  • 1
  • 6
  • 24
  • see the answers at http://stackoverflow.com/questions/17501556/returning-pointers-to-heap-objects-without-smart-pointers/17502369#17502369 – tp1 Aug 05 '13 at 12:50

1 Answers1

4

The problem is object slicing: when you return by value, you return an instance of that type, even if it is instantiated from a derived type:

CDeviceClientRequest getRequest(int type);

This can only return objects of type CDeviceClientRequest. You can fix this by returning a smart pointer to the base type.

std::unique_ptr<CDeviceClientRequest> getRequest(int type)
{
  switch (type)
  {
    GPS_SET_ENABLED: 
      return std::unique_ptr<CDeviceClientRequest>(new CDeviceGPSSetEnabledRequest);
    TS_SET_ENABLED : 
      return std::unique_ptr<CDeviceClientRequest>(new CDeviceTotalStationSetEnabledRequest);
    default:
   // raise an unknown type exception
  }

}

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Does that also mean that I change the invoke to void invoke(std::unique_ptr& request). Is there anything special I need to do to get the original instantiated class out of the unique_ptr? The object in question is going to end up in a method that will serialise it with boost. – Marcus MacWilliam Aug 05 '13 at 12:20
  • @MarcusMacWilliam you don't need to, references are polymorphic: a reference to a base class can refer to a derived object. Concerning "getting to the object" from the smart pointer, you can treat it as a pointer to get to its methods via `->`, it also has a de-reference operator `*`, so in principle you could serialize the contents of the object it manages. – juanchopanza Aug 05 '13 at 12:25
  • If I have getRequest() return a std::unique_ptr, and then pass the object to invoke with invoke(*request), will I not have the same problem. Will it not automatically upcast the CDeviceGPSSetEnabledRequest to a CDeviceClientRequest? – Marcus MacWilliam Aug 05 '13 at 12:41
  • @MarcusMacWilliam not if `invoke` takes a reference. – juanchopanza Aug 05 '13 at 12:51