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::visit
和 Visitor::visit(Derived2)
(或Visitor::visit(Derived2&)
)。
编辑
看到下面的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::visit
和 Visitor::visit(Derived2)
(或Visitor::visit(Derived2&)
)。
编辑