简体   繁体   中英

Inheritance and dynamic_cast of unique_ptr

I have a question that remains after studying this example: C++ ecosystem simulator (inheritance) .

Imagine that I have a similar example, where I want to work with the std::unique_ptr . Is there a way to do the dynamic casting in the example below, without having to resort to getting the raw pointer using the .get() on the std::unique_ptr ? I have put two variants in the code: one ( can_eat_1 ) that does it the old-fashioned way and one where I have a .get() in the dynamic_cast for which I wonder if it can be removed and replaced by a more elegant method ( can_eat_2 ):

#include <iostream>
#include <memory>

struct Animal
{
    virtual ~Animal() {};
};
struct Carnivore : public Animal {};
struct Herbivore : public Animal {};

struct Wolf   : public Carnivore {};
struct Rabbit : public Herbivore {};

bool can_eat_1(Animal* predator, Animal* prey)
{
    return ( dynamic_cast<Carnivore*>(predator) && dynamic_cast<Herbivore*>(prey) );
}

bool can_eat_2(std::unique_ptr<Animal>& predator, std::unique_ptr<Animal>& prey)
{
    return ( dynamic_cast<Carnivore*>(predator.get()) && dynamic_cast<Herbivore*>(prey.get()) );
}

int main()
{
    std::unique_ptr<Animal> wolf  (new Wolf  );
    std::unique_ptr<Animal> rabbit(new Rabbit);

    std::cout << "Option 1: pass raw pointers:" << std::endl;
    std::cout << "Wolf eats rabbit = " << can_eat_1(wolf.get(), rabbit.get()) << std::endl;
    std::cout << "Rabbit eats wolf = " << can_eat_1(rabbit.get(), wolf.get()) << std::endl;

    std::cout << "Option 2: pass unique_ptr:" << std::endl;
    std::cout << "Wolf eats rabbit = " << can_eat_2(wolf, rabbit) << std::endl;
    std::cout << "Rabbit eats wolf = " << can_eat_2(rabbit, wolf) << std::endl;

    return 0;
}

The guideline for smart pointers in function signatures is that they should appear there if and only if the function cares about the smart pointer itself, that is, the function is involved in object lifetime management.

std::unique_ptr<Foo> f();        // Factory (gives an object to the caller)
void f(std::unique_ptr<Foo> &p); // Factory via output parameter
void f(std::unique_ptr<Foo> p);  // Sink (takes an object from the caller)

In your case, the function checks a property of your animals. It doesn't care at all about their lifetime. Thus, the smart pointer should not appear in its signature.

void f(Foo const &p); // Observe a Foo
void f(Foo const *p); // Observe a Foo

Which one of the pointer or the reference you use is a matter of taste, but the usual choice here is a reference.

can_eat_3(*rabbit, *wolf);

You could try this via references:

bool can_eat_3(Animal const& predator, Animal const& prey)
{
    return
        dynamic_cast<Carnivore*>(&predator)
        &&
        dynamic_cast<Herbivore*>(&prey);
}

and call it via:

can_eat_3(*wolf, *rabbit);

If you dereference and get the address, you can work with can_eat_1, too:

can_eat_1(&*wolf, &*rabbit);

However, I do not really consider this 'more elegant'...

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