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

时间:2017-06-22 08:45:47

标签: c++ pointers base dynamic-cast derived

我看到了以下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;

}

有没有更好的方法来设计它,所以不需要强制转换,可以避免像这样的运行时错误?

5 个答案:

答案 0 :(得分:2)

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

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课程,您只需提供一个&#34;默认&#34;该方法的实现,而不是纯虚拟。

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

答案 1 :(得分:1)

如果我理解正确,你想在基类上调用派生类的函数。这个定义不明确。

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

答案 2 :(得分:0)

这里有一个很好的“功能性”可重用技巧:

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

的示例

答案 3 :(得分:0)

避免运行时错误。

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

当然,您无法将Base投射到Derived

答案 4 :(得分:0)

visitor pattern使用double dispatch允许您使用特定于类的成员函数和成员变量(与通过层次结构可用的成员函数/变量相对)。

要实现访问者模式,您需要访问者访问的层次结构(在您的示例中它是Base以及派生的类来自Base)。 用你的例子,它会给出类似的东西:

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的{​​{1}}知道它的真实类型,而多态变量(DerivedBase&)不是&#39 ;吨。覆盖Base*,您可以致电Base::visit,他们将向右visit发送电子邮件。

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

visit层次结构中添加新类型(例如Base类)时,您需要添加两个函数:Derived2Derived2::visit(或{ {1}})。

修改

live example