Call derived class non virtual member functions from base class pointer

我与影子孤独终老i 提交于 2019-12-01 12:10:39

I want to call derived class member functions which are present only in derived class & not in base class through base class pointer. How would I achieve this ?

You cannot call a non-virtual member function of the derived class with a pointer to the base class.

You'll need a pointer to the derived class. The simplest method is to use dynamic_cast to get a pointer to the derived class, check whether the cast was successful, then call the derived class member function using a derived class pointer.

A better method would be to provide a virtual member function in the base class and implement it in the derived class.

You can do what you want with dynamic_cast, but this will lead to disappointing results at a code review. Instead, I pitch you go the same route you did with printVehicle

class Vehicle
{
public:
    // without a virtual destructor you are walking into
    // a very bad bug. The wrong destructor may be called.
    virtual ~Vehicle()
    {
    } 
    virtual void printVehicle() = 0;

    // Specific stuff that all children must provide
    virtual void doTypeSpecificStuff() = 0;

    // this is actually a bit of a ideological weird. I'm not sure I can call
    // it a flaw. By making this factory function a member of Vehicle, Vehicle
    // must now know its children. If this is the case, the VehicleType enum
    // should probably be a member of Vehicle, but personally I think this
    // factory should be a totally free function.
    static Vehicle* Create(VehicleType type);
};
class TwoWheeler: public Vehicle
{
public:
    void printVehicle()
    {
        cout << "I am two wheeler" << endl;
    }
    void doTypeSpecificStuff()
    {
        cout << "Doing two wheeler stuff" << endl;
    }
};

Leaving out the other two classes and Vehicle::Create to save space.

int main()
{
    Vehicle* basePtr = Vehicle::Create(VT_TwoWheeler);
    basePtr->doTypeSpecificStuff(); //HOW TO ACHIEVE THIS CALL
    // leaking memory here, so
    delete basePtr;
    // but also look into std::unique_ptr. Much better suited to this behaviour


}

In fact, let's act on on that final comment about std::unique_ptr right now. A unique_ptr manages your dynamic allocations for you so you don't have to clutter up your code with deletes and run the risk of missing one or deleteing too soon. The unique_ptr's pointer is valid for as long as the unique_ptr is in scope. If you can compile, the pointer is good unless you done something silly like never point it at anything or manually remove the pointer.

And while we're at it, let's eliminate my earlier concerns about vehicle::Create.

First we define a free function to replace Create and return a unique_ptr. Since I hate to have to have if (ptr != NULL) checks all through my code to make sure an object really was created, let's also make a big stink about it when we can't match the provided vehicle type with class by throwing an exception.

And rather than a chain of if-else ifs we'll use a somewhat more elegant switch statement.

std::unique_ptr<Vehicle> SmarterVehicleFactory(VehicleType type)
{
    switch (type)
    {
        case  VT_TwoWheeler:
            return std::make_unique<TwoWheeler>();
        case  VT_ThreeWheeler:
            return std::make_unique<ThreeWheeler>();
        case  VT_FourWheeler:
            return std::make_unique<FourWheeler>();
        default:
            throw std::runtime_error("Invalid Vehicle type");
    }
}    

And then we'll use this new function

int main()
{
    try
    {
        std::unique_ptr<Vehicle> basePtr = SmarterVehicleFactory(VT_TwoWheeler);
        basePtr->doTypeSpecificStuff();

        basePtr = SmarterVehicleFactory(VT_ThreeWheeler); 
        // unique_ptr freed the TwoWheeler for us.
        basePtr->doTypeSpecificStuff(); 

        basePtr = SmarterVehicleFactory(VT_FourWheeler);
        basePtr->doTypeSpecificStuff(); 

        // just for laughs we will ask for a FiveWheeler, which we have not yet 
        // fully implemented
        basePtr = SmarterVehicleFactory(VT_FiveWheeler);  // will throw exception
        basePtr->doTypeSpecificStuff(); // will not be executed
    }
    catch (const std::exception & exc)
    {
        cerr << "Rats! Something bad happened: " << exc.what();
        // basePtr will be unmodified and still pointing to a FourWheeler
    }
} // basePtr  will go out of scope here and clean up our memory for us.

The beauty of this approach is no class knows anything about any other class. You can put Vehicle in a header with the SmarterVehicleFactory prototype and the list of vehicle types and hide everything else. The user sees nothing. Everybody is kept in the dark.

Why is that good? Because now you can change any of the above classes, except the Vehicle interface class, without having any effect on any of the other classes. This makes your code easier to maintain and debug.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!