如何覆盖作为函数输入的基 class 的方法?
How to override a method of a base class which is an input of a function?
我正在尝试重写代码中的一个方法,我正在使用它来了解这一点,但我没有成功,我错过了什么?
这里有一些代码可以重现我的问题:
my_class.h
#ifndef MY_CLASS_H
#define MY_CLASS_H
class Base {
public:
Base();
virtual ~Base();
virtual double evaluate(double x);
};
double call_evaluate(Base funct, double x);
#endif
my_class.cpp
Base::Base () {}
Base::~Base () {}
double Base::evaluate(double x){
std::cout << "evaluate base\n";
return 0.0;
};
double call_evaluate(Base funct, double x){
return funct.evaluate(x);
}
main.cpp
#include "my_class.h"
#include "iostream"
class Derived: public Base{
private:
double value;
public:
Derived(double value){
this->value = value;
}
double evaluate(double x) override{
std::cout << "evaluate derived\n";
return this->value*x;
}
};
int main(){
Derived problem(6);
double a=problem.evaluate(2);
double b=call_evaluate(problem, 2);
std::cout << "a = " << a << ", expected = 12\n";
std::cout << "b = " << b << ", expected = 12\n";
return 0;
}
Output
evaluate derived
evaluate base
a = 12, expected = 12
b = 0, expected = 12
这是一个多态性问题。
在您的示例中,基础 class 是 parent class,派生 class 是 child class.
A child class 具有从 parent class 继承的属性。因此,由于 属性 的传承。 (注意,反过来是不可能的。)但是,通过将 child class 的 object 传递给母亲 class 的 object,你输了child class.
相关的所有信息
在您的示例中,call_evaluate
函数采用 parent class 中的 object 作为参数。但您正试图从 child class 传递 object。这会丢失 child class 信息。
要解决您的问题,您必须添加对函数参数的引用,例如:
double call_evaluate(Base &funct, double x)
.
你也可以像这样传递一个指针:
double call_evaluate(Base *funct, double x)
.
简而言之,您需要更改 call_evaluate
以通过引用接收第一个参数。
double call_evaluate(Base& funct, double x);
有关您的程序如此运行的原因,请参阅下文。
您的编码风格表明您来自 Java-like 背景。您必须让自己意识到关于 C++ 的一个非常重要的观点。在下面声明的函数中,两个参数都是按值传递的,而不是按引用传递的。
double call_evaluate(Base funct, double x);
“按值”在当前的 C++ 说法中意味着将要复制源对象,即,将创建原始对象的未命名副本,然后将其传递给函数。 “通过引用”意味着源对象仅被引用或别名,也就是说,函数接收对同一对象的引用或别名。
从 Java-like 程序员的角度来看,这应该类似于下面的 pseudo-code。
auto tmp0 = funct.clone();
auto tmp1 = x.clone();
call_evaluate(tmp0, tmp1);
在 C++ 中,您可以通过使用 &
声明符将参数类型显式标记为引用类型来实现“通过引用”的效果。
double call_evaluate(Base& funct, double x);
对引用类型进行“通过引用”不需要在调用方进行特殊标记。
call_evaluate(func, x);
此外,在C和C++中,您可以通过将对象地址作为参数传递来实现“通过引用”的效果。为此,您可以使用 *
声明符将参数类型显式标记为指针类型。
double call_evaluate(Base* funct, double x);
使用指针类型“通过引用”调用需要调用者使用一元 &
运算符来获取对象的地址。
call_evaluate(&func, x);
现在,你的程序有什么问题?您正在“切片”一个多态对象。
由于call_evaluate
“按值”接收第一个参数,C++ 将生成一个“复制”表达式以从源对象创建一个新的、临时的、未命名的对象。
此“复制”表达式的目标类型将是参数的类型,Base
。换句话说,C++ 将生成一个表达式,目的是从源对象创建一个新的、临时的、未命名的 Base
类型的对象。
由于这个临时的、未命名的对象具有类型 Base
,您将很容易地看到它的多态行为就像任何 Base
类型应该的那样。
这称为“切片”,因为它“切片”了对象,也就是说,它仅从源对象的 Base
部分创建了一个新对象。
您可能会从这些实验中获益:
声明call_evaluate
为纯虚函数。如果baseclass里面没有定义,这个程序会做什么呢?有关详细信息,请参阅 https://en.cppreference.com/w/cpp/language/abstract_class
禁止使用 = delete
特殊语法的 Base
复制构造函数和复制赋值运算符。如果不允许复制Base
,编译器会做什么?有关详细信息,请参阅 https://en.cppreference.com/w/cpp/language/function#Deleted_functions
我正在尝试重写代码中的一个方法,我正在使用它来了解这一点,但我没有成功,我错过了什么?
这里有一些代码可以重现我的问题:
my_class.h
#ifndef MY_CLASS_H
#define MY_CLASS_H
class Base {
public:
Base();
virtual ~Base();
virtual double evaluate(double x);
};
double call_evaluate(Base funct, double x);
#endif
my_class.cpp
Base::Base () {}
Base::~Base () {}
double Base::evaluate(double x){
std::cout << "evaluate base\n";
return 0.0;
};
double call_evaluate(Base funct, double x){
return funct.evaluate(x);
}
main.cpp
#include "my_class.h"
#include "iostream"
class Derived: public Base{
private:
double value;
public:
Derived(double value){
this->value = value;
}
double evaluate(double x) override{
std::cout << "evaluate derived\n";
return this->value*x;
}
};
int main(){
Derived problem(6);
double a=problem.evaluate(2);
double b=call_evaluate(problem, 2);
std::cout << "a = " << a << ", expected = 12\n";
std::cout << "b = " << b << ", expected = 12\n";
return 0;
}
Output
evaluate derived
evaluate base
a = 12, expected = 12
b = 0, expected = 12
这是一个多态性问题。 在您的示例中,基础 class 是 parent class,派生 class 是 child class.
A child class 具有从 parent class 继承的属性。因此,由于 属性 的传承。 (注意,反过来是不可能的。)但是,通过将 child class 的 object 传递给母亲 class 的 object,你输了child class.
相关的所有信息在您的示例中,call_evaluate
函数采用 parent class 中的 object 作为参数。但您正试图从 child class 传递 object。这会丢失 child class 信息。
要解决您的问题,您必须添加对函数参数的引用,例如:
double call_evaluate(Base &funct, double x)
.
你也可以像这样传递一个指针:
double call_evaluate(Base *funct, double x)
.
简而言之,您需要更改 call_evaluate
以通过引用接收第一个参数。
double call_evaluate(Base& funct, double x);
有关您的程序如此运行的原因,请参阅下文。
您的编码风格表明您来自 Java-like 背景。您必须让自己意识到关于 C++ 的一个非常重要的观点。在下面声明的函数中,两个参数都是按值传递的,而不是按引用传递的。
double call_evaluate(Base funct, double x);
“按值”在当前的 C++ 说法中意味着将要复制源对象,即,将创建原始对象的未命名副本,然后将其传递给函数。 “通过引用”意味着源对象仅被引用或别名,也就是说,函数接收对同一对象的引用或别名。
从 Java-like 程序员的角度来看,这应该类似于下面的 pseudo-code。
auto tmp0 = funct.clone();
auto tmp1 = x.clone();
call_evaluate(tmp0, tmp1);
在 C++ 中,您可以通过使用 &
声明符将参数类型显式标记为引用类型来实现“通过引用”的效果。
double call_evaluate(Base& funct, double x);
对引用类型进行“通过引用”不需要在调用方进行特殊标记。
call_evaluate(func, x);
此外,在C和C++中,您可以通过将对象地址作为参数传递来实现“通过引用”的效果。为此,您可以使用 *
声明符将参数类型显式标记为指针类型。
double call_evaluate(Base* funct, double x);
使用指针类型“通过引用”调用需要调用者使用一元 &
运算符来获取对象的地址。
call_evaluate(&func, x);
现在,你的程序有什么问题?您正在“切片”一个多态对象。
由于call_evaluate
“按值”接收第一个参数,C++ 将生成一个“复制”表达式以从源对象创建一个新的、临时的、未命名的对象。
此“复制”表达式的目标类型将是参数的类型,Base
。换句话说,C++ 将生成一个表达式,目的是从源对象创建一个新的、临时的、未命名的 Base
类型的对象。
由于这个临时的、未命名的对象具有类型 Base
,您将很容易地看到它的多态行为就像任何 Base
类型应该的那样。
这称为“切片”,因为它“切片”了对象,也就是说,它仅从源对象的 Base
部分创建了一个新对象。
您可能会从这些实验中获益:
声明call_evaluate
为纯虚函数。如果baseclass里面没有定义,这个程序会做什么呢?有关详细信息,请参阅 https://en.cppreference.com/w/cpp/language/abstract_class
禁止使用 = delete
特殊语法的 Base
复制构造函数和复制赋值运算符。如果不允许复制Base
,编译器会做什么?有关详细信息,请参阅 https://en.cppreference.com/w/cpp/language/function#Deleted_functions