我刚刚意识到我过多地简化了代码并且它没有反映我真正的问题。我为不具体而道歉。我实际上要做的是以下几点:
#include<iostream>
class A
{
public:
A();
virtual void f()= 0;
void g();
};
A::A()
{
g();
}
void A::g()
{
f();
}
class B : public A
{
public:
B() {};
void f() {};
};
int main()
{
B b;
return 0;
}
我想程序调用纯虚函数A::f
,因为在调用构造函数时尚未创建B
。
这是正确的,我该如何克服这个问题?
请原谅我之前提出过于简化的问题。
答案 0 :(得分:5)
#include<iostream>
class A
{
virtual void f()=0;
public:
void g();
};
void A::g()
{
f();
}
class B : public A
{
void f(){std::cout<<"Inside B::f";}
};
int main()
{
B b;
b.g();
return 0;
}
输出
内部B :: f
发布您的真实代码,以获取真正的答案。
修改强>
现在,您向我们展示了您的真实代码(它有编译错误,我为您修复了它们)。我理解你的问题。
使用B
创建类B b;
的对象时,会导致调用基类构造函数A::A()
,而后者又调用方法g()
。请注意,g()
只是一个基类方法,因此会调用A::g()
。这进一步调用了f()
。现在你的期望是这应该调用B::f()
,但事实并非如此。
<强> WHY吗
请注意规则,
在构造函数或析构函数中,对象this
指向的类型始终是调用其构造函数/析构函数的类型。
应用上述规则,因为在f()
的构造函数中调用了A
,所以
this->f()
调用A::f()
而非B::f()
。此外,因为A::f()
没有定义(因为你没有提供定义)它会导致运行时异常:
称为纯虚方法 在没有活动异常的情况下终止调用。
编译器无法将此检测为编译时错误,因为virtual
要调用的函数在运行时确定,具体取决于this
指向的内容而非静态指向。编译器无法检测到这一点。
我如何克服这个问题?
您无法通过构造函数中的动态分派来完成此任务
如果从构造函数/析构函数之外的任何其他方法调用它,您可以实现并期望您期望的行为。
避免在构造函数和析构函数中调用虚函数,因为它们不会调用您可能会调用的版本。
答案 1 :(得分:0)
如果B :: f的函数签名与A :: f匹配,那么在A :: g中使用f()应该根据需要调用B :: f。在上面的示例中,它们匹配,A :: g应该调用B :: f。
如果您的真实函数签名比上面给出的示例更复杂,请确保它们的签名完全匹配,否则编译器会将B :: f视为新函数而不是覆盖A :: f。
*:严格来说,返回类型可能略有不同,但我希望这里不相关
答案 2 :(得分:0)
删除编译器错误后,your code works fine。
答案 3 :(得分:0)
从概念上讲,这应该有效。我已经很久没有使用过C ++了,但这与C#中的这种情况不一样:
public class MyBase
{
public virtual void PrintName() { }
public void DoWork()
{
PrintName();
}
}
public class MyDerivative : MyBase
{
public override void PrintName()
{
Console.WriteLine("MyDerivative");
}
}
答案 4 :(得分:0)
#include <iostream>
class A {
public:
virtual void f() = 0;
void g();
};
void A::g() {
std::cout << "A::g" << std::endl;
this->f();
}
class B : public A {
public:
virtual void f();
};
void B::f()
{
std::cout << "B::f" << std::endl;
}
int main() {
B b;
b.g();
return 0;
}
答案 5 :(得分:0)
在源代码中添加:
void B::f()
{
printf("im B::f\n");
}
并且您的计划应该有效。
您无法创建A的实例,因为存在纯虚方法。