C++ 如何在不使用 static_cast 或 dynamic_cast 的情况下从基 class 指针访问派生的 class 成员?

C++ How to access derived class member from base class pointer without using a static_cast or dynamic_cast?

看到下面的question就问自己有没有更好的办法解决这个问题,就不用强制转换了。 考虑以下代码:

#include <iostream>

class Base
{
    public:
        virtual ~Base() {}
};

class Derived : public Base
{
    protected:
        int someVar = 2;

    public:
        int getSomeVar () {return this->someVar;}   
};


int main()
{
    Base    B = Base(); 
    Derived D = Derived();

    Base *PointerToDerived  = &D;
    Base *PointerToBase     = &B;

    std::cout << dynamic_cast<Derived*>(PointerToDerived)->getSomeVar() << "\n"; //this will work
    std::cout << dynamic_cast<Derived*>(PointerToBase)->getSomeVar() << "\n"; //this will create a runtime error

    return 0;

}

有没有更好的方法来设计这个,这样就不需要转换并且可以避免这样的运行时错误?

是的,你可以在没有任何转换的情况下做到这一点:

class Base
{
    public:
        virtual ~Base() {}
        virtual int getSomeVar () = 0;
};

class Derived : public Base
{
    protected:
        int someVar = 2;

    public:
        virtual int getSomeVar () {return this->someVar;}   
};

int main()
{
//    Base    B = Base(); // will not compile, therefore you're safe
    Derived D = Derived();

    Base *PointerToDerived  = &D;
//    Base *PointerToBase     = &B;

    std::cout << PointerToDerived->getSomeVar() << "\n"; // no cast needed

    return 0;

}

如果您还希望能够构建 Base class,您可以只提供该方法的 "default" 实现,而不是让它成为纯虚拟的。

(另外,在这种特殊情况下,方法应该是 const

如果我没理解错的话,你想调用基于 class 的派生 class 的函数。那个定义不好。

相反,您可以在基 class 中声明一个虚函数,它将被派生 class 覆盖。有关详细信息,请参阅 Why do we need Virtual Functions in C++? and Wikipedia: Virtual Function

这里有一个不错的 'functional' 可重用技巧,您可以在此处应用:

template<typename Interface, typename Class, typename Function>
void with(Class * anObject, Function f) {
   if (auto * i = dynamic_cast<Interface*>(anObject))
     f(*i);
}

用法:

with<Derived>(PointerToBase, [](auto &derived) {
  std::cout << derived.getSomeVar() << "\n"; // wont be called
}
with<Derived>(PointerToDerived, [](auto &derived) {
  std::cout << derived.getSomeVar() << "\n"; // will be called
}

示例位于 http://cpp.sh/9vzis

避免运行时错误。

Derived* derived = dynamic_cast<Derived*>(PointerToBase);
if (derived != NULL) {
    std::cout << ->getSomeVar() << "\n"; 
}

当然,您不能将 Base 转换为 Derived

visitor pattern 使用双重调度允许您使用 class 特定的成员函数和成员变量(与通过层次结构可用的成员 function/variable 相反)。

要实现访客模式,您需要一个 visitor 和一个 visited 的层次结构(在您的示例中是 Base以及从 Base 派生的 classes)。 对于你的例子,它会给出类似的东西:

class Base
{
    public:
        virtual ~Base() {}
        virtual void visit(Visitor) = 0;
};

class Derived : public Base
{
    protected:
        int someVar = 2;

    public:
        int getSomeVar () {return this->someVar;}
        void visit(Visitor& v) {
            v.visit(this);
         }
};
class Visitor {
    public:
         void visit(Derived& d) {
              bar(d.someVar);
         }
};

访问者背后的想法是 this of Derived 知道它是真实类型,而多态变量(Base&Base*)不知道。覆盖 Base::visit 允许您调用 visit 谁将向右侧 Visitor::visit.

派遣

如果您不覆盖 Base::visit,当在 Base&(或 Base*)上调用它时,它将在 Base 对象上调用函数(因此this 将是 Base* 类型)。这就是为什么在示例中 Base::visit 是抽象的,否则只会使错误更容易发生(比如忘记覆盖 visit)。

Base 层级中添加新类型时(例如 Derived2 class),您需要添加两个函数:Derived2::visitVisitor::visit(Derived2)(或Visitor::visit(Derived2&))。

编辑

live example