【问题标题】:How to call exact derived class method?如何调用精确的派生类方法?
【发布时间】:2019-01-15 10:11:47
【问题描述】:

继续我之前的问题: How to return derived type?

我有一个 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???

我想要的相同的虚拟方法 name() 工作,但是 validateAttribute() 仅从基类调用。

【问题讨论】:

    标签: c++ polymorphism


    【解决方案1】:

    仔细看看你是如何定义void RequiredValidator::validateAttribute()

    您可能会注意到签名与基数略有不同(std::string &amp;attribute 不是 const

    有一个关键字 override 可以帮助编译器捕捉这些类型的拼写错误。

    【讨论】:

    • 需要我重新定义“虚拟”关键字吗?因为“错误:标记为'覆盖'的非虚拟成员函数隐藏了虚拟成员函数”
    • validateAttribute 签名在基类和派生类中必须相同
    • 只有一件事:对 `Validator::name[abi:cxx11]() const' 的未定义引用。如何解决?
    • @exru:它说它缺少virtual std::string name() const;的实现。
    • 谢谢兄弟,我自己修好了。它说什么: 1. 听编译器 2. 教英语 3. 阅读 docas 等用我糟糕的英语解决所有问题。但我正在尝试))而且我在 PHP 上花了很多时间。我只写了 2 个月的 C++)))谢谢你的帮助。
    【解决方案2】:
    • 首先,正如其他人指出的那样,

    §13.1 标准讨论了不能被 重载状态 -

    仅在存在或不存在时不同的参数声明 const 和/或 volatile 是等价的。即 const 和 volatile 忽略每个参数类型的类型说明符 [...]

    只有最外层的 const 和 volatile 类型说明符 以这种方式忽略参数类型规范;常量 以及隐藏在参数类型中的 volatile 类型说明符 规范是重要的,可以用来区分 重载函数声明。 [...]

    在确定声明、定义或调用哪个函数时。 “特别是,对于任何类型 T,“指向 T”、“指向 const T 的指针”, 和“指向 volatile 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;
    };
    

    编辑:附言。 override 需要 virtual 关键字。因此,

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

    【讨论】:

    • @exru,实际上,如果有 const 但没有引用,那么重载就可以了,即void validateAttribute(Model &amp;model, std::string attribute)void validateAttribute(Model &amp;model, const std::string &amp;attribute) 不会有这个问题。 (但当然,这不是我们想要做的......)我只是想详细说明一点。区别在于 referencespointers!对他们来说,const 会有所作为。
    • 谢谢兄弟。我修好了!
    • @JoeyMallone 你的意思是const std::string attribute 在你评论的第二个签名中吗?因为Tconst T 在签名中被破坏相同,但Tconst T&amp; 肯定不是...
    • @MaxLanghof,是的。但我现在无法编辑评论。 :( 我的意思是,void validateAttribute(Model &amp;model, std::string attribute)void validateAttribute(Model &amp;model, const std::string attribute) 可以重载。两个声明中都没有 &amp;
    【解决方案3】:

    删除所有不相关的代码,让我们看看你的基类和继承类的签名:

    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&amp; modelconst std::string&amp; attribute,在派生类的::validateAttribute() 中,有参数Model &amp;modelstd::string&amp; attribute

    这里的冲突是const std::string&amp;std::string&amp;。要么使这两个函数预先完全匹配,要么在你的情况下,因为你试图使用多态性,你可以在派生类中通过这样做来解决这个问题:

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

    这里的override 关键字将生成一个编译器错误,用于解释您的问题所在。如果您打算从这个派生类继承,请不要忘记添加virtual

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-31
      • 2013-04-11
      • 2014-01-27
      • 2021-10-09
      • 1970-01-01
      • 2013-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多