绑定如何用于C++中的分层继承?



>假设我有一个虚拟类:

class Tester {
virtual void Validate();
}
class Test1 : Tester {
void Validate(...) {
/implementation
}
}
class Test2 : Test1 {
void Validate(...) {
/implementation
}
}

据我了解,虚拟应该使其动态绑定。但我不确定如果我们有分层类会发生什么(即。测试2继承测试1继承测试器):

void RunTest(Tester test) {
test.Validate(...);
}
...
Test2 test_candidate = Test2();
RunTest(test_candidate)

这将调用哪种Validate方法?

void RunTest(Tester test)

函数的此参数是类型为Tester的对象。它不是Test1也不是Test2,因此调用不能是除Tester::Validate以外的任何东西。

当您将Test2作为参数传递时,它将通过复制基子对象转换为Tester

虚拟调度需要间接寻址。必须通过引用或指针调用函数,因为只有引用或指针才能指向派生对象的基子对象。

回答您的问题

(请参阅下面或此处,为什么您的示例代码的行为不是这样的)

"迟到"绑定的行为

后期绑定始终调用实例类型最专用的虚拟函数。在你的例子中,所有的类,TesterTest1Test2都会调用它们自己的Validate(),因为TestTest2在每个子类中覆盖这个函数。

但是,如果你有

Test3 : public Test1 { /* Not overriding Validate() */ }

Test3的实例上调用Validate()会调用Test1::Validate(),因为这是最专业的虚拟实现(它覆盖了Tester::Validate())。

示例代码中的问题

在示例代码中,函数RunTest(Tester)按值传递参数。如果您传递类型Test1的参数,它会创建类型Tester的临时副本,如其他答案所示,并且您的编译器根本不允许它而没有警告。这个临时副本在RunTest(Tester)中使用,并且将始终调用Tester::Validate(),这可能不是您作为参数传递给RunTest(Tester)的对象的实现。

若要使用后期绑定,应传递引用或指针:

RunTest(Tester *t) { t->Validate(); }

RunTest(Tester &t) { t->Validate(); }

使用其中之一将确保行为如上所述(您的答案)。

您提供的代码存在很多问题:

1. 私有继承 + 私有函数定义

您需要将方法声明为公共方法,以便能够从外部函数(如RunTest)调用它们。在这种情况下,您可能还希望公开继承,因此您可以将Test2类转换回Tester

例如:

class Tester {
public:
virtual void Validate();
};
class Test1 : public Tester {
public:
void Validate();
};
class Test2 : public Test1 {
public:
void Validate();
};

2. 一些建议

如果要重写子类中的虚拟方法,可以使用override将函数标记为这样。这样,如果函数无法被覆盖(例如,由于参数不匹配),您将收到编译时错误 (我假设你的意思是void Validate() {}而不是void Validate(...))

此外,如果您的类包含任何虚拟方法,则最好同时提供虚拟析构函数。(正确清理所有成员,以防它被指向其基类的指针删除)

例如:

class Tester {
public:
virtual void Validate() {
// TODO
}
virtual ~Tester() = default;
};
class Test1 : public Tester {
public:
void Validate() override {
// TODO 
}
};
class Test2 : public Test1 {
public:
void Validate() override {
// TODO
}
};

3.RunTest()函数将对 Test2 对象进行切片

您将一个Tester实例传递给 value.
RunTest(),这将导致对象被切片,即您将丢失派生对象中存储的所有内容。

void RunTest(Tester test);
// this
Test2 t2;
RunTest(t2);
// is equivalent to:
Test2 t2;
Test t = t2;
RunTest(t);

所以本质上你只用一个Test对象调用RunTest()方法,而不是Test2.

您可以通过引用或指针来解决此问题:

void RunTest(Tester& test) {}
// or
void RunTest(Tester* test) {}
<小时 />

工作示例

#include <iostream>
class Tester {
public:
virtual void Validate() {
std::cout << "Tester Validate" << std::endl;
}
virtual ~Tester() = default;
};
class Test1 : public Tester {
public:
void Validate() override {
std::cout << "Test1 Validate" << std::endl;
Tester::Validate();
}
};
class Test2 : public Test1 {
public:
void Validate() override {
std::cout << "Test2 Validate" << std::endl;
Test1::Validate();
}
};
void RunTest(Tester& test) {
test.Validate();
}
int main() {
Test2 t;
RunTest(t);
}

在神霹雳上测试一下!

最新更新