Как вернуть производный тип? - PullRequest
0 голосов
/ 15 января 2019

У меня есть класс Validator и производные классы от него; Когда я пытаюсь вернуть указатель на производный класс, метод возвращает базовый класс (Validator) вместо Derived.

class Validator
{
public:
    std::string m_name = "BaseValidator";

    static const std::map<std::string, Validator *> validators();

    static Validator *getByName(std::string &name);
};


const std::map<std::string, Validator*> Validator::validators()
{
    std::map<std::string, Validator*> result;
    //RequiredValidator is derived
    result["required"] = new RequiredValidator();
    return result;
}

Validator* Validator::getByName(std::string &name)
{
    auto g_validators = Validator::validators();
    auto validator = g_validators.find(name);
    if(validator != g_validators.end()){
        std::cout << "getByName: " << validator->second->m_name << std::endl;
        return validator->second;
    }else{
        std::cerr << "Unknow type of validator: " << name << std::endl;
    }
    return nullptr;
}

//output BaseValidator but i need RequiredValidator


class RequiredValidator : public Validator
{
public:
    std::string m_name = "RequiredValidator";
};

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Вы объявили две переменные-члены с именем m_name, одну в Validator и одну в RequiredValidator. За исключением одного и того же имени, эти две переменные совершенно не связаны. Ваш компилятор, вероятно, напечатает предупреждение о втором слежении за первым.

К какой переменной вы обращаетесь, зависит от типа переменной, к которой вы обращаетесь.

Например:

RequiredValidator r;
std::cout << r.m_name << "\n"; // prints "RequiredValidator"
Validator* v = &r;
std::cout << v->m_name << "\n"; // prints "BaseValidator"
std::cout << dynamic_cast<RequiredValidator*>(v)->m_name << "\n"; // prints "RequiredValidator"

Есть несколько решений для этого. Первый - просто установить значение переменной BaseValidator в конструкторе RequiredValidator:

class Validator
{
public:
   std::string m_name;
   Validator( const std::string& name = "BaseValidator" )
   :m_name( name )
   {
   }
};

class RequiredValidator : public Valdiator
{
public:
    RequiredValidator()
    : Validator("RequiredValidator")
    {}
};

Более обычное решение - использовать вместо этого виртуальный метод:

class Validator
{
public:
  virtual std::string getName() { return "BaseValidator"; }
};

class RequiredValidator : public Valdiator
{
public:
  virtual std::string getName() override { return "RequiredValidator"; }
};
0 голосов
/ 15 января 2019

Возвращает производный экземпляр, но поскольку validator является Validator*, вы смотрите на m_name член Validator, а не на RequiredValidator.
(Несмотря на то, что они имеют одинаковые имена, они являются разными переменными. Виртуальных переменных не существует.)

Есть несколько вариантов;

  • Вы можете иметь виртуальную функцию getName и переопределить ее в каждом подклассе.

  • Установите base m_name в производных классах, например, путем присвоения имени параметру базового конструктора.

Пример: * * тысяча двадцать-пять

class Validator
{
public:
    Validator(const std::string& name = "BaseValidator") : m_name(name) {};
    // ...
};

class RequiredValidator : public Validator
{
public:
    RequiredValidator() : Validator("RequiredValidator") {}
    // ...
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...