A need for dynamic cast of a derived class: looking for an alternative approach

北战南征 提交于 2019-12-24 03:32:43

问题


I present my question in this simple form:

class animal {
public:
    animal() {
        _name="animal";
    }

    virtual void makenoise(){
        cout<<_name<<endl;
    }

    string get_name(){
        return _name;
    }

protected:
    string _name;
};

class cat : public animal {
public:
    cat() {
        this->_name="cat";
    }
};

class dog : public animal {
public:
    dog() {
        this->_name = "dog";
    }
};

I want to store all animal types together in a single container such as:

vector<animal*> container;
barnyard.push_back(new animal());
barnyard.push_back(new dog());
barnyard.push_back(new cat());

At some point in my code, I need to convert a dog object into a cat object. And all I need from this converting is to set up a fresh dog object and replace it at the same index number as a cat counterpart was located. As I understood, dynamic_cast wouldn't work in this case and based on C++ cast to derived class, it's mentioned that such a conversion is not a good practice. Since cat and dog in my model have distinct behavioral properties, I don't want to put their definitions into the animal model. On the other hand, storing them separately in different vectors would be difficult to handle. Any suggestions?


回答1:


You say:

I need to convert a dog object into a cat object.

But then:

And all I need from this converting is to set up a fresh dog object and replace it at the same index number as a cat counterpart was located.

Do you need to convert it or replace it?? That's a completely different operation.

To convert you need to setup a function that will take a dog and return a cat:

auto convertDogToCat(Dog const& dog) -> Cat {
    auto cat = Cat{};

    // fill cat's member using dog's values...

    return cat; 
}

But to replace simply reassign with a new one:

//      v--- a cat is currently there
barnyard[ii] = new Dog{};
//           ^--- we replace the old pointer
//                with one that points to a dog.

But that creates a memory leak, to remove the leak, simply use std::unique_ptr:

#include <memory> // for std::unique_ptr

// The base class need a virtual destructor
class animal {
public:
    virtual ~animal() = default;

    // other members...
};

std::vector<std::unique_ptr<animal>> barnyard;
barnyard.emplace_back(std::make_unique<animal>());
barnyard.emplace_back(std::make_unique<dog>());
barnyard.emplace_back(std::make_unique<cat>());

barnyard[ii] = std::make_unique<Dog>();



回答2:


Here’s an alternative approach. Doesn’t use OOP or dynamic dispatch, but provides equal functionality to your sample. Also much faster, because no dynamic memory is required to allocate/free, animals are single bytes.

enum struct eAnimalKind : uint8_t
{
    Generic = 0,
    Cat = 1,
    Dog = 2,
};

string get_name( eAnimalKind k )
{
    static const std::array<string, 3> s_names =
    {
        "animal"s, "cat"s, "dog"s
    };
    return s_names[ (uint8_t)k ];
}

void makenoise( eAnimalKind k )
{
    cout << get_name( k ) << endl;
}

If your classes keep more state than a type, use one class with that enum as a member.

If some animals use custom set of fields/properties it gets tricky but still possible, nested structures for specie-specific state, and std::variant of these structures inside class animal to get track on the specie and keep the data. In this case you no longer need enum eAnimalKind, std::variant already tracks the type it contains.

Classic C++ OOP requires dynamic memory. Derived classes generally have different sizeof, you can’t keep them in a single vector you can only keep pointers, and in runtime you’ll hit RAM latency on accessing every single element.

If your animals are large and complex i.e. megabytes of RAM and expensive methods, that’s fine. But if your animals are small, contain a couple of strings/numbers, and you have a lot of them, RAM latency will ruin the performance of OOP approach.



来源:https://stackoverflow.com/questions/57042518/a-need-for-dynamic-cast-of-a-derived-class-looking-for-an-alternative-approach

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