2 votes

C++ Initialiser std::vector<std::unique_ptr<Base>> dans les classes dérivées

J'ai une classe de base polymorphe qui stocke une std::vector<std::unique_ptr<Base>> . J'aimerais initialiser ce vecteur dans les classes dérivées. Les tailles de ces vecteurs sont connues à la compilation et ne changent pas après la construction. Dans ma solution actuelle, j'ai une fonction virtuelle pure initialize() qui est remplacée dans chaque produit dérivé :

class Base {
public:
    virtual void Initialize() = 0;
    virtual ~Base() = default;
protected:
    Base(std::size_t count) : m_data(count) {}
    std::vector<std::unique_ptr<Base>> m_data;
};

class Derived1 : public Base {
public:
    Derived1() : Base{ 8 } {}
    void initialize() override {
        m_data[0] = std::make_unique<Derived1>();
        // ...
        m_data[7] = std::make_unique<Derived1>();
    };
};

class Derived2 : public Base {
public:
    Derived2() : Base{ 24 } {}
    void initialize() override {
        m_data[0] = std::make_unique<Derived2>();
        // ...
        m_data[23] = std::make_unique<Derived2>();
    };
};

Cependant, je ne suis pas satisfait de cette solution ; en partie à cause de l'utilisation d'une fonction virtuelle et de la redondance de la fonction
m_data[0] = ...; ... m_data[N-1] = ... .
Je souhaite attribuer m_data comme ceci :

m_data = {
    std::make_unique<Derived1>(),
    // ...
    std::make_unique<Derived1>()
}

qui ne fonctionne pas à cause de std::unique_ptr s deleted copy ctor.

Voici un exemple plus réaliste de mon code

Quelle est la meilleure façon d'initialiser le vecteur dans les classes dérivées ? J'utilise C++17.

0voto

user2079303 Points 4916

Cependant, je ne suis pas satisfait de cette solution ; en partie à cause de l'utilisation d'une fonction virtuelle

Si la fonction virtuelle pose problème, rendez-la non virtuelle.

et la redondance

Vous pouvez simplement utiliser une boucle pour vous débarrasser des affectations répétées :

for (auto& ptr : m_data)
    ptr = std::make_unique<Derived1>();

Pour éviter la redondance d'un code identique pour chaque type, il existe un outil pour cela en C++, les modèles :

template<class D, std::size_t N>
struct BaseT : Base {
    BaseT() : Base{N} {}
    void Initialize() override {
        for (auto& ptr : m_data)
            ptr = std::make_unique<D>();
    };
};

struct Derived1 : BaseT<Derived1, 8> {};
struct Derived2 : BaseT<Derived2, 24> {};

Dans ce cas, l'argument du modèle est le type dérivé. Il existe un nom pour ce type d'idiome : Curiously Recurring Template Pattern (modèle curieusement récurrent).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X