简体   繁体   中英

In C++ polymorphism, how to avoid name hiding for functions (no matching function for call error)?

I'm in an intro to C++ class and I'm trying to find out why I'm getting a "no matching function for call" error. And I've looked through other posts, but those mainly seem to be problems with the constructors themselves.

This is simplified snippets:

In Base Class - Ship

// Members: shipName, shipBuiltYear
Ship::Ship(){ //implementation }
Ship::Ship(string name, string year){ //implementation }
void Ship::set(string name, string year){ //implementation }

In Derived Class - PirateShip

// Members: numPirates
PirateShip::PirateShip() : Ship() { //implementation }
PirateShip::PirateShip(string name, string year, string pirates) : ship(name, year){ //implementation }
void PirateShip::set(int pirates){ //implementation }

In main

Ship *ships[2] = {new Ship(), new PirateShip()};
ships[0] -> set("Luvinia", "2020"); // using setter from base class
ships[1] -> set("Skylin", "2030"); // using setter from base class
ships[1] -> set(100); // using setter from derived class

Is the problem that you can't use the base class to set the PirateShip, then use the PirateShip to set it again?

Do I have to change:

void PirateShip::set(int pirates){ //implementation }

to:

void PirateShip::set(string name, string year, string pirates)
{ 
    Ship::set(name, year);
    numPirates = pirates; 
}

?

Or is there another way to do this?

There are two problems here.

First:

Ship *ships[2] = {new Ship(), new PirateShip()};

this is an array of pointers to the Ship part of two objects. The first is a Ship , the second a PirateShip . But you only have a pointer to the Ship part of the PirateShip , so can only (directly) interact with it.

If Ship has virtual methods, then you could use RTTI and dynamic_cast to query if a given Ship* is pointing to the Ship part of a PirateShip like this:

auto* pirate = dynamic_cast<PirateShip*>(some_ship);

if pirate is non-null, then some_ship pointed at an Ship piece of a PirateShip .

Note that using dynamic cast is code smell; it probably means you should either improve the base class interface, or not have a pointer to base class here.


The second part is if you want to be able to call Ship::set from PirateShip* , you need to add

using Ship::set;

to the body of PirateShip .

If you know that an element is a specific type (and you must know that if you are calling with its specific parameter requirements) then you can cast to the appropriate type:

Ship* ships[2] = {new Ship(), new PirateShip()};
ships[0] -> set("Luvinia", "2020"); // using setter from base class
ships[1] -> set("Skylin", "2030"); // using setter from base class
dynamic_cast<PirateShip*>(ships[1]) -> set(100); // change to the correct interface

However I would question the design here. For polymorphism to work well, you should not need to know the exact types of the sub classes after creation .

Perhaps make the initialization call part of the constructor? Or configure the objects before adding them to the array?

ALSO

Using raw pointers to own objects is not considered good practice these days. I recommend a smart pointer :

std::unique_ptr<Ship> ships[2];

Also using built in arrays is similarly less than optimal in most cases. Consider a std::vector :

std::vector<std::unique_ptr<Ship>> ships;

Or a std::array if you want a fixed size:

std::array<std::unique_ptr<Ship>, 2> ships;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM