Можно ли поддерживать вызовы метода polymorphi c одновременно с абстрагированием указателей от пользователей? - PullRequest
0 голосов
/ 17 апреля 2020

Проблема

Я пытаюсь поддерживать некоторую степень полиморфизма в моем коде. Я в основном хочу переопределить метод str в подклассах (Dog и Duck) и затем использовать умный указатель на суперкласс Animal в другом классе (AnimalContainer) для вызова метода str. В частности, я хочу поддержать следующий API:

int main(){
    Duck duck;
    Dog dog;

    AnimalContainer duckInContainer(duck);
    std::cout << duckInContainer.str() << std::endl; // Outputs "None" but should output "Duck"


    AnimalContainer dogInContainer(dog);
    std::cout << dogInContainer.str() << std::endl; // Outputs "None" but should output "Dog"
}

Отсутствие указателей здесь является преднамеренным. Я намерен сделать это интерфейсом API и хочу сделать его максимально простым. Поэтому, если возможно, я хочу, чтобы пользователи явно не создавали (скажем) указатель Dog. Другими словами, я бы хотел избегать заставления пользователей делать что-то вроде этого:

Dog dog;
std::unique_ptr<Dog> dog_ptr = std::make_unique<Dog>(dog);
AnimalContainer animalContainer(dog_ptr)
std::cout << animalContainer.str() << std::endl;

Возможно ли вышеизложенное, и если да, то какие изменения мне нужно внести в следующее код:

код

#include <iostream>
#include <vector>
#include <memory>

using namespace std;


class Animal {

public:
    Animal() = default;

    virtual std::string str() {
        return "None";
    }

};

class Duck : public Animal {
public:
    using Animal::Animal;

    std::string str() override {
        return "Duck";
    }
};

class Dog : public Animal {
public:
    using Animal::Animal;

    std::string str() override {
        return "Dog";
    }
};

typedef std::unique_ptr<Animal> AnimalPtr;


class AnimalContainer {
private:
    AnimalPtr animal_ptr;
public:
    explicit AnimalContainer(const Animal& animal){
        this->animal_ptr = std::make_unique<Animal>(animal);
    }

    explicit AnimalContainer(AnimalPtr animal_ptr){
        this->animal_ptr = std::make_unique<Animal>(*animal_ptr);
    }

    std::string str(){
        return this->animal_ptr->str();
    }

};

До сих пор я думаю, что эта строка

this->animal_ptr = std::make_unique<Animal>(animal);

является проблемой, так как Dog или Duck нарезается до Animal. Тем не менее, альтернатива, которая является явным о том, какой тип животного мы хотим в указателе ака

std::unique_ptr<Dog> dog_ptr = std::make_unique<Dog>(Dog())

1 Ответ

1 голос
/ 17 апреля 2020

Без функции клонирования вы можете сделать:

class AnimalContainer {
private:
    AnimalPtr animal_ptr;
public:
    explicit AnimalContainer(const Dog& dog){
        this->animal_ptr = std::make_unique<Dog>(dog);
    }
    // Same for Duck

    explicit AnimalContainer(AnimalPtr animal_ptr){
        this->animal_ptr = std::move(animal_ptr));
    }

    std::string str(){ return this->animal_ptr->str(); }
};

Или более обобщенно:

class AnimalContainer {
private:
    AnimalPtr animal_ptr;
public:
    template <typename T>
    explicit AnimalContainer(const T& animal){
        this->animal_ptr = std::make_unique<T>(animal);
    }

    explicit AnimalContainer(AnimalPtr animal_ptr){
        this->animal_ptr = std::move(animal_ptr));
    }

    std::string str(){ return this->animal_ptr->str(); }
};
...