C++ C++;
假设我有以下课程:C++ C++;,c++,inheritance,C++,Inheritance,假设我有以下课程: class A { }; class B : public A { }; class C : public A { }; class BaseClass { public: virtual void dummy(A* a) = 0; }; class DerivedClass1 : public BaseClass { public: void dummy(B* b); }; class DerivedClass2 : public BaseClas
class A {
};
class B : public A {
};
class C : public A {
};
class BaseClass {
public:
virtual void dummy(A* a) = 0;
};
class DerivedClass1 : public BaseClass {
public:
void dummy(B* b);
};
class DerivedClass2 : public BaseClass {
public:
void dummy(C* c);
};
正如您看到的B
和C
继承自A
和DerivedClass1
和DerivedClass2
继承自BaseClass
根据我的理解,这种函数声明dummy()
是不允许的,因为参数已经更改,即使B
和C
是从A
派生的
正确的方法是什么
一种可能是将参数保留在基类中,然后在dummy
函数中进行强制转换,但我想这不是很好
否则,只需不派生dummy
(并将其从BaseClass
中删除),因此这两个类DerivedClass1
和DerivedClass2
都独立声明它们。但是从BaseClass
派生的所有类都必须实现dummy的想法就没有了。首先,当声明某个A
是B
或C
的基类时,您这样做的目的是使B
和C
具有A
的所有属性,并且您可以在中使用A
的任何位置使用B
和C
。一个好的设计通常为A
提供一个有意义的定义,允许对A
的所有专门化进行dummy
的一般定义。从这个意义上讲,这个问题可能没有你想象的那么普遍
如果你想实例化DerivedClass1
或DerivedClass2
,你的DerivedClass1
和DerivedClass2
的概念实际上没有意义,因为为了成为基类
,它们必须提供一个有意义的无效虚拟(a*a)
定义。你必须提供这个定义。它们可以提供额外的方法void dummy(B*B)
或void dummy(C*C)
,但这些方法仅在基类
实现的静态类型为DerivedClass1
或DerivedClass2
时使用
如果DerivedClass1
或DerivedClass2
需要区分B
和C
,这可能是设计不良的标志。但这也可能是一个明智的决定。通常,您需要一种方法来动态地分派到正确的方法。例如,这可以通过以下方式实现:
class DerivedClass1 : public BaseClass
{
virtual void dummy( A * a ) override {
if( dynamic_cast<B*>( a ) != nullptr ) {
dummy( dynamic_cast<B*>( a );
}
else if( dynamic_cast<C*>( a ) != nullptr) {
dummy( dynamic_cast<C*>( a );
}
else
{
// ...
}
}
void dummy( B * );
void dummy( C * );
};
class-DerivedClass1:公共基类
{
虚拟无效虚拟(A*A)覆盖{
如果(动态_cast(a)!=nullptr){
假人(动态铸型(a);
}
否则如果(动态_cast(a)!=nullptr){
假人(动态铸型(a);
}
其他的
{
// ...
}
}
虚空假人(B*);
空洞假人(C*);
};
根据我的理解,这种函数dummy()的声明是不允许的
从技术上讲,所显示的dummy
声明是允许的。但可能是您希望孩子们是具体的,而事实并非如此
否则,只需不派生dummy(并将其从基类中移除),这样DerivedClass1和DerivedClass2两个类都独立声明它们
这种方法是有道理的
但是,从基类派生的所有类都必须实现dummy的想法就消失了
这只是一个不能通过继承基类来表达的想法。没有“child使用某个参数实现成员函数”的概念。参数类型必须是已知的,并且所有子级必须接受基类接受的所有参数。这不是由层次结构实现的,因此子级保持抽象
您正在寻找的结构似乎是概念。C++20将引入一种编程方式来指定概念;在此之前,概念是隐式的:如果您编写模板,您可以指定类型参数必须满足成员函数dummy
的要求类型将满足这个概念。虚拟继承的要点是,您可以通过对基类型的引用/指针来调用派生类:
BaseClass* p = some_factory();
因此,在调用p.dummy(…)
时,您(不一定)知道指针后面的运行时类型。因此,您无法明智地决定是否必须向其传递A
、B
或C
实例
这就是为什么您必须匹配声明类型,并在重写函数中找出您拥有的实际对象
static\u cast
:如果您确信调用者将向您传递正确的运行时类型
dynamic\u cast
:如果不这样做,则会导致运行时成本和要求rtti
- 或您的
A
类型具有虚拟成员函数,您只需通过A*
参数调用即可
例如:
void DerivedClass1::dummy(A* a) {
if (B* const b = dynamic_cast<B*>(a)) {
/* do stuff with b */
} else {
/* error handling -> wrong runtime type */
}
}
void-DerivedClass1::dummy(A*A){
如果(B*常数B=动态(a)){
/*和b一起做事*/
}否则{
/*错误处理->错误的运行时类型*/
}
}
C++无法处理您的情况,因为无法对基类的函数调用施加此类约束。但是可以更改虚拟函数的类型,例如,对于返回的类型,请参阅:
类基类{
公众:
虚空剂量(A*)=0;
虚拟A*虚拟(A*)=0;
};
类DerivedClass1:公共基类{
公众:
void dosomething(A*A){B*B=dummy(A);做某事;}
//请参见返回类型是A*但现在是B*
B*虚拟(A*A){返回动态_cast(A);}
};
你的问题到底是什么?那不是继承的目的。如果你有BaseClass*
somewhe
class BaseClass {
public:
virtual void dosomething(A*) = 0;
virtual A* dummy(A*) = 0;
};
class DerivedClass1 : public BaseClass {
public:
void dosomething(A* a) {B* b = dummy(a); do something;}
//See return type was A* but now is B*
B* dummy(A* a) { return dynamic_cast<B*>(a); }
};