Pass subclasses to a function that takes their superclass

14,804

Solution 1

This is how it should look.

Dog* spike = new Dog();
Cat* puss = new Cat();

function(*spike, *puss);

void function(Animal pet, Animal pet2)
{
   //magic
}

Tested and working.

Solution 2

When you dynamically allocate a new object, you get a pointer to that object. So you need to store it in a pointer like so:

Dog* spike = new Dog();
Cat* puss = new Cat();

You can then pass spike or puss for any parameter of type Animal*, assuming Dog and Cat do indeed inherit from Animal. This is the basics of polymorphism in C++:

A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D.

You could, of course, have stored them right away as Animal*:

Animal* spike = new Dog();
Animal* puss = new Cat();

Don't forget to delete them. Better yet, don't use new at all:

Dog spike;
Cat puss;
void function(const Animal&, const Animal&);
function(spike, puss);

Solution 3

It's reasonable to assume that the problem you have is assigning a pointer to a non-pointer, or vice versa. But your code is not the real code, and your error messages are apparently not the real error messages. So it's all guesswork, in particular those already-posted answers that say "this is it" (it probably is, but not necessarily, and the uncertainty is entirely your own fault).


EDIT: the OP changed the question's code 10 seconds after I posted this.

The code still does not square with the purported error message.

I'm not going to chase this question as it changes.


Now, as to what to do…


Don't use new.

Experienced C++ programmers sometimes use new in controlled ways, wrapped in suitable code. Incompetent C++ programmers often use new as a matter of course. But in general, you don't need it, and it's problematic, so better as default don't use it.

Then, your program (which you neglected to show) would look like this:

#include <iostream>

struct Animal {};
struct Dog: Animal {};
struct Cat: Animal {};

void function(Animal const& pet1, Animal const& pet2 )
{
   //magicAndSoForth();
}

int main()
{
    Dog spike; 
    Cat puss;

    function( spike, puss );
}

Solution 4

Your prototype for function almost certainly says

void function(Animal pet1, Animal pet2);

or something very similar to that. (I know you have a prototype, since function appears after main. If you hadn't forward-declared it, C++ would complain that it couldn't find function at all, not that it's taking the wrong types of args.)

Problem is, your real function takes pointers. And since main appears before the real function, it doesn't see that. It only sees a declaration of one that takes actual Animals, so it tries to use that...but fails, because an Animal pointer is not an Animal. (The real function differing from the prototype is fine with C++, due to the possibility of overloading. As far as the compiler knows, function(Animal, Animal) exists in another translation unit, and you're just defining function(Animal*, Animal*) too.)

Look through your code for the declaration of function, and make it say

void function(Animal *pet1, Animal *pet2);

to match up with the actual function's signature.

PS: this would have been so much easier to figure out if you had included all the relevant declarations.

PPS: A better idea would be to take references instead, as suggested by Alf. But in order to do that anyway, you'd have to fix the prototype mismatch (or make the real function appear before code that uses it) first

Share:
14,804
XistenZ
Author by

XistenZ

Updated on June 04, 2022

Comments

  • XistenZ
    XistenZ about 2 years

    Let's say I have three classes - Animal, Cat and Dog, where Cat and Dog are subclasses of Animal (this does sound like the first lectures, but it's not homework I promise, just simplifying the real code)

    Dog* spike = new Dog(); 
    Cat* puss = new Cat();   
    
    int main(int argc, char** argv)
    {
        function(spike, puss);
        return 0;
    }
    
    void function(Animal *pet, Animal *pet2)
    {
       magic->andSoForth();
    }
    

    Now this generates the following error:

    Cannot convert parameter 1 from 'Dog *' to 'Animal'
      No constructor could take the source type,
        or constructor overload resolution was ambiguous
    

    Changing the parameters to exactly match generates similar errors, only that it says it can't convert from a class to the same class.

    I have successfully called the subclasses functions and members that they inherit from the superclass, so I know that this, logically, should work. I just don't know in what twisted way this language want me to bend logic.

    EDIT

    Solution happen to be: pointers confuse everyone.

    • Declare pointers.
    • Send pointers as arguments to a function that does NOT handle pointers.

    In my example, I sent the "not-pointers" to the function that wanted pointers, I just switched that. Now it works fine.

  • Xymostech
    Xymostech over 11 years
    Do you mean Animal *spike?
  • Nicomoto
    Nicomoto over 11 years
    Sorry, it is Animal * Spike, my mistake, Ive edited it in now..Thanks @Xymostech
  • XistenZ
    XistenZ over 11 years
    I do actually declare them as pointers in the real code, sorry I was sloppy doing the mock-up :) I tried to change them to Animal as suggested, but it made no difference. Still the same error :/
  • Joseph Mansfield
    Joseph Mansfield over 11 years
    @XistenZ Append to your question the actual code and the line that the error says it corresponds to.
  • XistenZ
    XistenZ over 11 years
    I neglected to show my program since I'm just after the principle. I do send parameters when creating the different classes, can you do that with structs? Don't know much about them yet, except they're confusingly alot like classes, but uglier.
  • sehe
    sehe over 11 years
    @XistenZ But uglier? I'm all ears (Hint: "C++" != "C#", not even string.Equals("C++", "C#", StringComparison.UniversalPolyglotIgnoreInterpunction))
  • Puppy
    Puppy over 11 years
    They are, for all meaningful purposes, entirely identical to classes.
  • R. Martinho Fernandes
    R. Martinho Fernandes over 11 years
    @XistenZ I don't know what your sources are, but rest assured, structs are as ugly as classes are. The only difference between the two is that one has public visibility by default, and the other has private.
  • XistenZ
    XistenZ over 11 years
    Sorry, didn't mean to offend anyone ^^ I meant ugly just visually, can't really explain how though.
  • Joseph Mansfield
    Joseph Mansfield over 11 years
    @XistenZ Your edit does not explain what you did to fix it. Show what was wrong and the code that fixed it. Also, put the answer in your answer, not in the question. :) Thanks
  • cHao
    cHao over 11 years
    @XistenZ: In C++, structs are classes. Literally, the only differences between a struct and a class are the default visibility (public for structs) and the use of struct rather than class to declare the type. In every other respect, they are identical. Even inheritance, virtual functions, and all that classy stuff; structs can do all that too, identically.
  • cHao
    cHao over 11 years
    The problem with this is that it forgets that, for example, pet2 is a Cat. In this case it's not a huge deal, because the animals don't actually do much if anything. But later on, when a Cat includes custom behavior and properties, or if you want to squirrel one away in a vector or something, you'll experience the "slicing problem" firsthand. The correct answer involves figuring out why the pointer version breaks; it should not be as complicated as you're making it out to be.
  • cHao
    cHao over 11 years
    Or, declaring the function to take references. That'd be better than pointers, but still doesn't fix the reason why you're currently stuck passing Animals by value. And you need to figure that out; with C++, you don't get polymorphism unless you access an object through a pointer or reference. Without polymorphism, subclasses become rather useless.