В чем проблема с этой заводской функцией? - PullRequest
0 голосов
/ 09 января 2020

В приведенном ниже коде я пишу фабричный метод для создания объектов типа в иерархии классов.


#include <iostream>
#include <memory>
using namespace std;

enum Type {
    _Base, _A, _B, _C
};

class Base{
private:
    Type type = _Base;
public:
    virtual Type getType(){
        return type;
    }};

class A : public Base{
private:
    Type type = _A;
public:
    using Base::Base;
};

class B : public Base{
private:
    Type type = _B;
public:
    using Base::Base;
};

class C : public Base{
private:
    Type type = _C;
public:
    using Base::Base;
};

shared_ptr<Base> letterFactory(Type which){
    shared_ptr<Base> base = make_unique<Base>(Base());
    switch (which){
        case _A:
            base = make_unique<Base>(A());
        case _B:
            base = make_unique<Base>(A());
        case _C:
            base = make_unique<Base>(C());
    }
    return base;
}


int main(){
    shared_ptr<Base> instanceOfA = letterFactory(_A);
    cout << instanceOfA->getType() << endl;

    shared_ptr<Base> instanceOfB = letterFactory(_B);
    cout << instanceOfB->getType() << endl;

    shared_ptr<Base> instanceOfC = letterFactory(_C);
    cout << instanceOfC->getType() << endl;

    return 0;
};

Вывод

0
0
0

Как мне сделать вывод

1
2
3

Ответы [ 3 ]

3 голосов
/ 09 января 2020

Ваша фабрика имеет некоторые недостатки, и ваша функция getType() не переопределена в производных классах. Я предполагаю, что вы хотели сделать что-то вроде этого:

#include <iostream>
#include <memory>

using namespace std;

enum Type {
    _Base, _A, _B, _C
};

class Base{
public:

    virtual ~Base() = default;
    virtual Type getType() const {
        return _Base;
    };
};

class A : public Base{
public:
    virtual Type getType() const override {
        return _A;
    };
};

class B : public Base{
public:
    virtual Type getType() const override {
        return _B;
    };
};

class C : public Base{
public:
    virtual Type getType() const override {
        return _C;
    };
};

unique_ptr<Base> letterFactory(Type which){
    switch (which){
        case _Base:
            return make_unique<Base>();
        case _A:
            return make_unique<A>();
        case _B:
            return make_unique<B>();
        case _C:
            return make_unique<C>();
    }

    return nullptr;
}


int main(){
    shared_ptr<Base> instanceOfA = letterFactory(_A);
    cout << instanceOfA->getType() << endl;

    shared_ptr<Base> instanceOfB = letterFactory(_B);
    cout << instanceOfB->getType() << endl;

    shared_ptr<Base> instanceOfC = letterFactory(_C);
    cout << instanceOfC->getType() << endl;

    return 0;
};

Обратите внимание, что мы полностью избавились от члена type и вместо этого должным образом отвергли функцию getType(). Кроме того, такие заводские функции обычно возвращают unique_ptr (который может быть неявно преобразован в shared_ptr, если вы действительно хотите).

2 голосов
/ 09 января 2020

Ваш Base класс имеет член type и функцию виртуального члена getType(), которая возвращает значение члена type. Ваши классы A, B и C происходят от Base. Это означает, что все они имеют подобъект Base. Этот подобъект содержит член Base::type. Кроме того, все они также добавляют еще один элемент type, который никогда больше не используется. Кроме того, ни один из них не отменяет метод getType. Поэтому всякий раз, когда вы вызываете

instanceOfX->getType()

, даже если instanceOfX указывает на экземпляр одного из производных классов, поскольку ни один из производных классов не перезаписывает getType, вы в конечном итоге вызовете Base::getType, который вернет значение Base::type, которое всегда равно _Base

То, что вы на самом деле хотели, было, вероятно, что-то вроде:

struct Base
{
    virtual Type getType() const = 0;

protected:
    Base() = default;
    Base(Base&&) = default;
    Base(const Base&) = default;
    Base& operator =(Base&&) = default;
    Base& operator =(const Base&) = default;
    ~Base() = default;
};

class A : public Base
{
public:
    Type getType() const override { return _A; }
};

class B : public Base
{
public:
    Type getType() const override { return _B; }
};

class C : public Base
{
public:
    Type getType() const override { return _C; }
};

Обратите внимание, что это почти наверняка плохой дизайн. Единственная цель, которой мог бы служить такой метод getType, - чтобы клиентский код мог определить конкретный тип объекта, на который указывает Base*. Если вам когда-нибудь понадобится эта информация, ваш дизайн нарушает принцип подстановки Лискова

Помимо всего этого, обратите внимание, что _Base, _A, _B и _C зарезервированные имена [lex.name] / 3 , которые вы не должны использовать в коде C ++…

1 голос
/ 09 января 2020

Я немного реорганизовал ваш код, чтобы применить некоторые из лучших практик:

  • использовать enum class вместо необработанных enum, поскольку enum class представляет тип перечисления с областью видимости, и это также строго типизированный, что означает, что вы не можете легко преобразовать его в целое число как необработанный enum (вот почему у нас есть to_integral шаблонная функция)
  • предпочитают не использовать using namespace std;, потому что он импортирует все Пространство имен std в текущее пространство имен программы
  • также, я думаю, вы хотели бы использовать std::make_shared вместо std::make_unique
#include <memory>
#include <iostream>

enum class Type : int {
    _Base = 0,
    _A    = 1,
    _B    = 2,
    _C    = 3
};

class Base{
private:
    Type type = Type::_Base;
public:
    virtual Type getType(){
        return type;
    }
};

class A : public Base{
private:
    Type type = Type::_A;
public:
    virtual Type getType() override {
        return type;
    }
};

class B : public Base{
private:
    Type type = Type::_B;
public:
    virtual Type getType() override {
        return type;
    }
};

class C : public Base{
private:
    Type type = Type::_C;
public:
    virtual Type getType() override {
        return type;
    }
};

std::shared_ptr<Base> letterFactory(Type which){
    switch (which){
        case Type::_A:
            return std::make_shared<A>();
        case Type::_B:
            return std::make_shared<B>();
        case Type::_C:
            return std::make_shared<C>();
        default:
            return std::make_shared<Base>(Base());
    }
}

template <typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value,
                                  typename std::underlying_type<Enum>::type>::type
to_integral(Enum const& value) {
    return static_cast<typename std::underlying_type<Enum>::type>(value);
}

int main(){
    std::shared_ptr<Base> instanceOfA = letterFactory(Type::_A);
    std::cout << to_integral(instanceOfA->getType()) << std::endl;

    std::shared_ptr<Base> instanceOfB = letterFactory(Type::_B);
    std::cout << to_integral(instanceOfB->getType()) << std::endl;

    std::shared_ptr<Base> instanceOfC = letterFactory(Type::_C);
    std::cout << to_integral(instanceOfC->getType()) << std::endl;

    return 0;
};

Живой пример

...