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

В продолжение моего предыдущего вопроса: Как вернуть производный тип?

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

Я изменил свой класс следующим образом:

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

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

    void validateModel(Model &model, const std::vector<std::string> &attrNames);
    static Validator *getByName(std::string &name);

    virtual void validateAttribute(Model &model, const std::string &attribute);
    virtual ~Validator();

    virtual std::string name() const;
};

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

void Validator::validateModel(Model &model, const std::vector<std::string> &attrNames)
{      
     validateAttribute(model, name.at(0));    
}

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

void Validator::validateAttribute(Model &model, const std::string &attribute)
{
    (void)model;
    (void)attribute;
    std::cout << this->name() << std::endl;
}

//------------------

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

    void validateAttribute(Model &model, std::string &attribute);
    ~RequiredValidator();
    std::string name() const;
};


std::string RequiredValidator::name() const
{
    return m_name;
}

void RequiredValidator::validateAttribute(Model &model, std::string &attribute)
{
    (void) model;
    (void) attribute;
    std::cout << "RequiredValidator!!!" << std::endl;
}

RequiredValidator::~RequiredValidator()
{

}


//------------------

auto validator = Validator::getByName("required");

validator->name();// will output RequiredValidator

//next i'm calling 

validator->validateModel(); // whos calling validateAttribute() inside
//and it's output Validator::validateAttribute() //but i wan't
//RequiredValidator::validateAttribute(); 
//where i was wrong with logic???

То же имя виртуального метода () работает, как я хочу, но validateAttribute () вызывается только из базового класса.

Ответы [ 3 ]

0 голосов
/ 15 января 2019
  • Во-первых, как уже отмечали другие,

§13.1, где Стандарт обсуждает заявления, которые не могут быть перегруженные состояния -

Объявления параметров, которые отличаются только наличием или отсутствием const и / или volatile эквивалентны. То есть const и volatile спецификаторы типа для каждого типа параметра игнорируются [...]

Только const и volatile-спецификаторы типов на самом внешнем уровне спецификация типа параметра игнорируется таким образом; Const и изменяемые спецификаторы типа, скрытые в типе параметра спецификации важны и могут использоваться для различения перегруженные объявления функций. [...]

при определении, какая функция объявляется, определяется или вызывается. «В частности, для любого типа T« указатель на T »,« указатель на const T » и «указатель на изменчивый T» считается различными типами параметров, как «ссылка на Т», «ссылка на констант Т» и «ссылка на Летучий Т ».

Следовательно,

void validateAttribute(Model &model, std::string &attribute) override;

не совпадает с

void validateAttribute(Model &model, const std::string &attribute);
  • Во-вторых, вы можете избежать подобных ошибок, если у вас есть C++11 или новее с использованием ключевого слова override в объявлении функции, как показано ниже.

С ключевым словом override этот код не позволит вам скомпилировать, если сигнатура функции в производном классе не совпадает с сигнатурой в базовом классе!

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

    void validateAttribute(Model &model, std::string &attribute) override;
    ~RequiredValidator();
    std::string name() const;
};

РЕДАКТИРОВАТЬ: p.s. override требуется ключевое слово virtual. Следовательно,

virtual void validateAttribute(Model &model, std::string &attribute) override;
0 голосов
/ 15 января 2019

Удаление всего нерелевантного кода давайте рассмотрим подписи как базовых, так и унаследованных классов:

class Validator {
public:
    virtual void validateAttribute(Model &model, const std::string &attribute);
};

class RequiredValidator : public Validator {
public:
    void validateAttribute(Model &model, std::string &attribute);
};

В базовом классе ::validateAttribute() у вас есть для его параметров Model& model и const std::string& attribute, а в производном классе ::validateAttribute() у вас есть для его параметров Model &model и std::string& attribute.

Конфликт здесь const std::string& и std::string&. Либо сделайте две функции точно совпадающими заранее, либо в вашем случае, потому что вы пытаетесь использовать полиморфизм, который вы можете в своем производном классе исправить, выполнив следующее:

/*virtual*/ void validateAttribute( Model& model, std::string& string ) override;

Ключевое слово override здесь сгенерирует ошибку компилятора, объясняя, в чем ваша проблема. Не забудьте также добавить virtual, если вы планируете наследовать от этого производного класса.

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

Посмотрите внимательно на то, как вы определили void RequiredValidator::validateAttribute()

Вы можете заметить, что подпись немного отличается от базовой (std::string &attribute не const)

есть ключевое слово override, которое поможет компилятору перехватывать эти типы опечаток.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...