您遇到了我们称之为对象切片的情况,这是具有值语义的语言(如 C++)中的常见问题。
当您将子类型的值分配给connection
超类型的新位置(您的变量)时,就会发生对象切片。这引入了实例的新副本,但它是超类型,而不是子类型,因此您丢失了有关您想要实例化的具体类的信息。
为避免这种情况,您有多种选择。
经典方法使用指针:
Base * connection;
connection = new YourConcreteType();
然后,要使用此对象,您必须使用星号运算符 ( ) 取消对它的引用*
:
(*connection).function();
connection->function(); // syntactic sugar, semantically equivalent
不要忘记:您必须在使用后删除对象:
delete connection;
为了简化这一点,C++ 引入了两个概念:引用和智能指针。虽然前者有一个限制,只能分配一次,但它是语法上最简单的一个。后者类似于指针方法,但您不必关心删除,因此您不太可能遇到内存泄漏情况:
std::shared_ptr<Base> connection;
connection = make_shared<YourConcreteType>(); // construction via 'make_shared'
// ... use as if it were just a pointer ...
connection->function();
// no delete!
还有其他“智能指针”类型,例如unique_ptr
,如果您不打算传递指针(如果它保持在范围内),可以使用它们。
现在,您可以分别实现两个类中的功能。为了利用多态性,这意味着在运行时,要么调用一个子类的函数,要么调用另一个子类的函数,这取决于构造了哪个,你应该将基类中的函数声明为 is virtual
,否则,函数Base
无论您构造的具体类型如何,都会调用定义。
在您的情况下,您想要调用一个应该根据类型执行不同操作的函数。虽然您的方法是引入两个不同的函数,即A
and B
,但您可以只声明一个函数,让我们将其handleEvent
称为基类中的纯虚拟(=抽象)函数,这意味着“此函数将在子类中实现classes”,并在两个子类中独立定义:
Base {
....
virtual void handleEvent(...) = 0; // "= 0" means "pure"
};
// Don't provide an implementation
Client {
void handleEvent(...); // override it
};
// define it for Client:
void Client::handleEvent(...) {
...
}
Host {
void handleEvent(...); // override it
};
// define it for Host:
void Host::handleEvent(...) {
...
}