class Base
{
public:
virtual void print() = 0;
};
class A : public Base
{
int mClassA;
public:
A() : mClassA(1) {}
void print() override { std::cout << "print A" << std::endl; }
void foo( A& arg ) { std::cout << mClassA << std::endl; }
};
class B : public Base
{
int mClassB;
public:
B() : mClassB(2) {}
void print() override { std::cout << "print B" << std::endl; }
void foo( B& arg ) { std::cout << mClassB << std::endl; }
};
所以我得到了类似的类结构。我应该采用什么方法在没有dynamic_cast的情况下每次调用foo?
int main()
{
Base * obj1 = new A();
Base * obj2 = new A();
dynamic_cast<A*>(obj1)->foo(*dynamic_cast<A*>(obj2));
}
我可以使用基类参数创建foo方法,但我想确保我将A或B obejct作为参数传递。
您可以使用模板来确保其中一个类成员函数的特定参数至少具有特定类型。请参阅以下代码说明:
template <class P>
class Base
{
public:
Base(int nr) : mClass(nr) {}
virtual void print() = 0;
virtual void foo( P& arg ) { std::cout << mClass << std::endl; }
protected:
int mClass;
};
class A : public Base<A>
{
public:
A() : Base(1) {}
void print() override { std::cout << "print A" << std::endl; }
virtual void foo( A& arg ) override { Base::foo(arg); cout << "is A for sure" << endl; }
};
class B : public Base<B>
{
public:
B() : Base(2) {}
void print() override { std::cout << "print A" << std::endl; }
virtual void foo( B& arg ) override { Base::foo(arg); cout << "is B for sure" << endl; }
};
int main()
{
Base<A> * obj1 = new A();
A* obj2 = new A();
obj1->foo(*obj2);
Base<B> * objb1 = new B();
B* objb2 = new B();
objb1->foo(*objb2);
// objb1->foo(*obj2);
// Non-const lvalue reference to type 'B' cannot bind to a value of unrelated type 'A'
}
听起来你想做这样的事情:
class Base
{
public:
virtual void foo(Base&) = 0;
};
class A : public Base
{
public:
void foo(A&);
};
class B : public Base
{
public:
void foo(B&);
};
在面向对象的设计中,这被称为covariance(具体地,“协变方法参数类型”)。
问题是这违背了良好的面向对象设计的原则。 Liskov substitution principle说,如果你有一个基类Base
,那么Base
的子类的任何实例都需要是可以互换的 - 但你想要一些Base
的子类不能与Base
的其他子类一起使用。 (这是一个过于简单化的问题,但网上有很多关于更详细的讨论。)
如果你想这样做 - 如果它是你案例中最好的解决方案,尽管有Liskov替换原则的一般建议 - 那么你可以自己实施检查。
void A::foo(Base& base_arg) {
// This will throw a runtime exception if the wrong type
A& arg = dynamic_cast<A&>(base_arg);
std::cout << mClassA << std::endl;
}
请注意,您现在牺牲了一些编译时类型安全性 - 如果您不小心尝试使用A::foo
实例调用B
,则在代码运行之前您将无法知道并且您将获得异常。 (这是虚函数/动态调度/多态的全部意义 - 行为是在运行时确定的。)
另一种方法是使用模板,如@Stephen Lechner's solution。这放弃了运行时多态性,但它保持了强大的类型安全性,并且更好地遵循传统的OO设计。
Wikipedia article on covariance有更多的讨论,包括进一步的示例代码。