正在调用基础 class 函数,而不是派生 class 中定义的重写函数
Base class function being called instead of overridden function defined in Derived class
这是我的代码
class BaseClass
{
public:
BaseClass() {}
void init(const int object) { cout<<"BaseClass::init"<<endl; }
void run(const int object) { cout<<"BaseClass::run calls =>"; init(object); }
};
class Derived : public BaseClass {
public:
Derived() {}
void init(const int object) { cout<<"Derived::init"<<endl; }
};
int main() {
BaseClass b;
b.init('c');
b.run('c');
Derived d;
d.init(5); // Calls Derived::init
d.run(5); // Calls Base::init. **I expected it to call Derived::init**
}
这是生成的输出
BaseClass::init
BaseClass::run calls =>BaseClass::init
Derived::init
BaseClass::run calls =>BaseClass::init
通过调用 d.run(5),为什么调用“BaseClass::init”而不是“BaseClass::init”?
我虽然只有在通过指针调用时才需要虚函数。
保持这种行为背后的理由是什么?
I though we need virtual functions only when calling through a pointer.
没有。那是不对的。当您想要根据对象的动态类型调用它时,您需要将函数设为虚函数。一旦你有了虚函数,你就可以使用指针或引用来利用虚拟分派。
当你想重写一个方法时,你需要使它成为虚拟的。由于 BaseClass::run
未被 Derived::run
覆盖,因此不存在从 BaseClass::run
调用 BaseClass::init
的虚拟分派和调用 init
。如果要为 BaseClass::init
启用虚拟调度,则需要将其声明为虚拟。
此外,考虑到您的代码等同于:
void run(const int object) {
cout<<"BaseClass::run calls =>";
this->init(object);
}
this
是 BaseClass*
,因此调用 BaseClass::init
。您正在通过指针调用 init
,但这并不重要,因为 init
不是虚拟的。如果您想根据对象的动态类型调用 init
,那正是 virtual
的用武之地。您在评论中提到的“开销”并不是真正的开销,而只是获得您想要的行为所需的最低限度。如果您可以更改设计并且不需要运行时多态性,您可以看看 CRTP,它是静态多态性的一种形式。
Why "BaseClass::init" is being called instead of "BaseClass::init" ?
因为init
是一个non-virtual成员函数。为了达到预期的效果,你需要让init
成为一个虚拟成员函数,如下所示:
class BaseClass
{
public:
BaseClass() {}
//NOTE THE VIRTUAL KEYWORD HERE
virtual void init(const int object) { cout<<"BaseClass::init"<<endl; }
//other member function here as before
};
I though we need virtual functions only when calling through a pointer.
请注意,语句 init(object);
等同于 写作
this->init(object); //here `this` is a pointer
当你写道:
d.run(5);
在上面的语句中,first对象d
的地址隐式作为第一个参数传递给隐式 this
成员函数的参数run
。此隐式 this
参数的类型是 BaseClass*
,这是由于 派生到基础转换 。现在,调用 init(object);
等同于 this->init(object);
。但是由于 init
是 non-virtual 成员函数,调用在 编译时 解析,这意味着基数 class init
将被调用。
基本上,当使用派生 class 对象调用成员函数时,编译器首先查看该成员是否存在于派生 class 中。如果没有,它开始沿着 继承链 和 检查 成员是否已在任何父 class 中定义。它使用它找到的第一个。这意味着对于调用 d.run(5)
,对名为 run
的成员函数的搜索从派生的 class 开始。但是由于在派生 class 中没有名为 run
的函数,编译器查看直接基 class BaseClass
并找到名为 run
的成员函数。所以它停止搜索并使用这个 found run
成员函数。正如我所说,在您的示例中,init
是 non-virtual,因此调用在编译时解析为基础 class run
.
另一方面,如果我们让init
成为一个虚成员函数,那么这个调用将在[=82处解析=] 表示派生的 class init
将被调用。
这是我的代码
class BaseClass
{
public:
BaseClass() {}
void init(const int object) { cout<<"BaseClass::init"<<endl; }
void run(const int object) { cout<<"BaseClass::run calls =>"; init(object); }
};
class Derived : public BaseClass {
public:
Derived() {}
void init(const int object) { cout<<"Derived::init"<<endl; }
};
int main() {
BaseClass b;
b.init('c');
b.run('c');
Derived d;
d.init(5); // Calls Derived::init
d.run(5); // Calls Base::init. **I expected it to call Derived::init**
}
这是生成的输出
BaseClass::init
BaseClass::run calls =>BaseClass::init
Derived::init
BaseClass::run calls =>BaseClass::init
通过调用 d.run(5),为什么调用“BaseClass::init”而不是“BaseClass::init”? 我虽然只有在通过指针调用时才需要虚函数。
保持这种行为背后的理由是什么?
I though we need virtual functions only when calling through a pointer.
没有。那是不对的。当您想要根据对象的动态类型调用它时,您需要将函数设为虚函数。一旦你有了虚函数,你就可以使用指针或引用来利用虚拟分派。
当你想重写一个方法时,你需要使它成为虚拟的。由于 BaseClass::run
未被 Derived::run
覆盖,因此不存在从 BaseClass::run
调用 BaseClass::init
的虚拟分派和调用 init
。如果要为 BaseClass::init
启用虚拟调度,则需要将其声明为虚拟。
此外,考虑到您的代码等同于:
void run(const int object) {
cout<<"BaseClass::run calls =>";
this->init(object);
}
this
是 BaseClass*
,因此调用 BaseClass::init
。您正在通过指针调用 init
,但这并不重要,因为 init
不是虚拟的。如果您想根据对象的动态类型调用 init
,那正是 virtual
的用武之地。您在评论中提到的“开销”并不是真正的开销,而只是获得您想要的行为所需的最低限度。如果您可以更改设计并且不需要运行时多态性,您可以看看 CRTP,它是静态多态性的一种形式。
Why "BaseClass::init" is being called instead of "BaseClass::init" ?
因为init
是一个non-virtual成员函数。为了达到预期的效果,你需要让init
成为一个虚拟成员函数,如下所示:
class BaseClass
{
public:
BaseClass() {}
//NOTE THE VIRTUAL KEYWORD HERE
virtual void init(const int object) { cout<<"BaseClass::init"<<endl; }
//other member function here as before
};
I though we need virtual functions only when calling through a pointer.
请注意,语句 init(object);
等同于 写作
this->init(object); //here `this` is a pointer
当你写道:
d.run(5);
在上面的语句中,first对象d
的地址隐式作为第一个参数传递给隐式 this
成员函数的参数run
。此隐式 this
参数的类型是 BaseClass*
,这是由于 派生到基础转换 。现在,调用 init(object);
等同于 this->init(object);
。但是由于 init
是 non-virtual 成员函数,调用在 编译时 解析,这意味着基数 class init
将被调用。
基本上,当使用派生 class 对象调用成员函数时,编译器首先查看该成员是否存在于派生 class 中。如果没有,它开始沿着 继承链 和 检查 成员是否已在任何父 class 中定义。它使用它找到的第一个。这意味着对于调用 d.run(5)
,对名为 run
的成员函数的搜索从派生的 class 开始。但是由于在派生 class 中没有名为 run
的函数,编译器查看直接基 class BaseClass
并找到名为 run
的成员函数。所以它停止搜索并使用这个 found run
成员函数。正如我所说,在您的示例中,init
是 non-virtual,因此调用在编译时解析为基础 class run
.
另一方面,如果我们让init
成为一个虚成员函数,那么这个调用将在[=82处解析=] 表示派生的 class init
将被调用。