派生中纯虚基方法的特化class
Specialization of pure virtual base method in derived class
我想知道是否有一个通用的 way/pattern 让派生的 class 在给定的基础 class.[=17 中有一个更专业的纯虚方法版本=]
class Base {
public:
Base() = default;
virtual ~Base() = 0;
virtual void foo(int bar) = 0;
};
inline Base::~Base() {}
class Derived public Base {
public:
Derived() = default;
~Derived() = default;
void foo(int bar) override {/*...*/}
};
class SpecializedDerived : public Base {
public:
SpecializedDerived() = default;
~SpecializedDerived() = default;
void foo(int bar, double additionalParameter) override {/*...*/}
};
SpecializedDerived
class中的override是不可能的,因为方法的签名与纯虚Base
class中的不对应。
现在,有没有办法实现所描述的设计?有没有一种方法可以实现 "more specialized methods" 因为有 class 继承可以让你实现 "more specialized classes"?
打字的时候我想我的愿望更像是一种"Dude, I just want you to provide an iterate(.)
function of some sort!"东西。
到目前为止我想到的唯一想法是
class Base {
public:
Base() = default;
virtual ~Base() = 0;
virtual void foo(int bar) = 0;
};
inline Base::~Base() {}
class SpecializedDerived : public Base {
public:
SpecializedDerived(double addParam) : additionalParam_(addParam) {}
~SpecializedDerived() = default;
void foo(int bar) override {
iterate(bar, additionalParam_);
return;
}
private:
double additionalParam_;
void foo(int bar, double additionalParam) {/*...*/}
};
这个内部函数调用实际上是多余的,您可以这样做:
class SpecializedDerived : public Base {
public:
SpecializedDerived(double addParam) : additionalParam_(addParam) {}
~SpecializedDerived() = default;
void foo(int bar) override {/* code using additionalPara_ */}
private:
double additionalParam_;
};
从阅读 Wikipedia+ contravariance 开始。这个想法是 child 可以用一个接受更广泛接口的函数覆盖一个函数。该函数必须接受旧接口,但也可以扩展它。这个想法是不打破对基数 class.
具有 reference/pointer 的代码的期望
请注意,目前C++不支持逆变,所以这只是学术讨论。
您代码中的覆盖函数不接受对旧接口的调用,因此不算作逆变。
已覆盖:
virtual void foo(int bar):
覆盖:
void foo(int bar, double additionalParameter) override {/*...*/} };
正确的方法是在C++中手动遵循逆变原则。这或多或少是你试图做的。您需要覆盖:
void foo(int bar) override
并让它调用带有附加参数的函数。对于 classes 的用户来说,这似乎是逆变(带有一个额外的默认参数)。
注意:用常规函数重载虚函数是危险的,并且会导致问题。重载函数会隐藏 parent 中具有相似名称的函数。除非小心处理,否则这可能会导致混乱和错误。 override
关键字使问题不那么严重,但仍然存在一些风险。
为什么需要 matchnig 签名?
多态性和虚函数背后的思想是,调用者不必知道它使用的对象的真实 class 的任何细节:
示例:
Base *my_object = find_dynamically_a_relevant_object (...);
my_object->foo(10); // could be a Derived or a SpecializedDerived
std::vector<Base*> container;
... // somehow you populate the container with a mix of
... // Derived AND SpecializedDerived objects
for (auto x: container)
x->foo(std::srand());
这就是为什么签名必须与基础 class 中定义的签名完全匹配的原因。
但是也可以使用不同的签名吗?
现在您可以很好地定义重载 foo()
,在三种情况下具有完全不同的签名:
- 不是
override
:它是同名的不同函数。 * 仅当您确定对象具有正确的类型时,您才可以使用其附加参数调用重载的 foo()。
- 您必须确保在提供匹配签名的情况下提供覆盖(因为它是纯虚拟的)。例如,这可以只调用您的重载,为额外参数使用一些任意值)
示例:
class SpecializedDerived : public Base {
public:
SpecializedDerived() = default;
~SpecializedDerived() = default;
void foo(int bar) override { foo(bar, 0.0); }
void foo(int bar, double additionalParameter) {cout<<"specialized "<<bar<<" "<<additionalParameter<<endl;}
};
... // elsewhere, for example in main():
SpecializedDerived my_obj;
my_obj.foo(10); // use the override of the base
my_obj.foo(10, 37.2); // use the overload
// suppose that p is a Base* like in the first example
auto *q = dynamic_cast<SpecializedDerived*>(p);
if (q) // dynamic cast makes this nullptr if not the right type
q->foo(10, 37.2);
else cout << "Not specialized"<<endl;
根据一些附加数据用行为覆盖
现在,如果您想在严格的多态上下文中使用您的 foo() 但仍然有一些(隐藏的)附加参数,则有几种可能性,例如:
- 您可以扩展基本签名并添加一个附加的、大部分未使用的具有默认值的参数。在大多数情况下,这是一个坏主意:如果新派生 class 带有另一个参数怎么办。
- 您可以在调用之前注入附加参数(按照您自己的建议在构建时注入,或者在需要时使用 setter 更改值)。适用于大多数情况。唯一的风险是确保在调用
foo()
之前正确设置了附加参数
- 您可以更改签名以将包含所有实际参数的对象用作单个参数。这需要一些额外的开销,但是对于可变参数非常灵活。唯一的问题是这个对象的类型可能也需要是多态的,在这种情况下,您必须确保将正确的参数类型用于正确的对象。在我看来,它超级强大,但超级超级危险。
我想知道是否有一个通用的 way/pattern 让派生的 class 在给定的基础 class.[=17 中有一个更专业的纯虚方法版本=]
class Base {
public:
Base() = default;
virtual ~Base() = 0;
virtual void foo(int bar) = 0;
};
inline Base::~Base() {}
class Derived public Base {
public:
Derived() = default;
~Derived() = default;
void foo(int bar) override {/*...*/}
};
class SpecializedDerived : public Base {
public:
SpecializedDerived() = default;
~SpecializedDerived() = default;
void foo(int bar, double additionalParameter) override {/*...*/}
};
SpecializedDerived
class中的override是不可能的,因为方法的签名与纯虚Base
class中的不对应。
现在,有没有办法实现所描述的设计?有没有一种方法可以实现 "more specialized methods" 因为有 class 继承可以让你实现 "more specialized classes"?
打字的时候我想我的愿望更像是一种"Dude, I just want you to provide an iterate(.)
function of some sort!"东西。
到目前为止我想到的唯一想法是
class Base {
public:
Base() = default;
virtual ~Base() = 0;
virtual void foo(int bar) = 0;
};
inline Base::~Base() {}
class SpecializedDerived : public Base {
public:
SpecializedDerived(double addParam) : additionalParam_(addParam) {}
~SpecializedDerived() = default;
void foo(int bar) override {
iterate(bar, additionalParam_);
return;
}
private:
double additionalParam_;
void foo(int bar, double additionalParam) {/*...*/}
};
这个内部函数调用实际上是多余的,您可以这样做:
class SpecializedDerived : public Base {
public:
SpecializedDerived(double addParam) : additionalParam_(addParam) {}
~SpecializedDerived() = default;
void foo(int bar) override {/* code using additionalPara_ */}
private:
double additionalParam_;
};
从阅读 Wikipedia+ contravariance 开始。这个想法是 child 可以用一个接受更广泛接口的函数覆盖一个函数。该函数必须接受旧接口,但也可以扩展它。这个想法是不打破对基数 class.
具有 reference/pointer 的代码的期望请注意,目前C++不支持逆变,所以这只是学术讨论。
您代码中的覆盖函数不接受对旧接口的调用,因此不算作逆变。 已覆盖:
virtual void foo(int bar):
覆盖:
void foo(int bar, double additionalParameter) override {/*...*/} };
正确的方法是在C++中手动遵循逆变原则。这或多或少是你试图做的。您需要覆盖:
void foo(int bar) override
并让它调用带有附加参数的函数。对于 classes 的用户来说,这似乎是逆变(带有一个额外的默认参数)。
注意:用常规函数重载虚函数是危险的,并且会导致问题。重载函数会隐藏 parent 中具有相似名称的函数。除非小心处理,否则这可能会导致混乱和错误。 override
关键字使问题不那么严重,但仍然存在一些风险。
为什么需要 matchnig 签名?
多态性和虚函数背后的思想是,调用者不必知道它使用的对象的真实 class 的任何细节:
示例:
Base *my_object = find_dynamically_a_relevant_object (...);
my_object->foo(10); // could be a Derived or a SpecializedDerived
std::vector<Base*> container;
... // somehow you populate the container with a mix of
... // Derived AND SpecializedDerived objects
for (auto x: container)
x->foo(std::srand());
这就是为什么签名必须与基础 class 中定义的签名完全匹配的原因。
但是也可以使用不同的签名吗?
现在您可以很好地定义重载 foo()
,在三种情况下具有完全不同的签名:
- 不是
override
:它是同名的不同函数。 * 仅当您确定对象具有正确的类型时,您才可以使用其附加参数调用重载的 foo()。 - 您必须确保在提供匹配签名的情况下提供覆盖(因为它是纯虚拟的)。例如,这可以只调用您的重载,为额外参数使用一些任意值)
示例:
class SpecializedDerived : public Base {
public:
SpecializedDerived() = default;
~SpecializedDerived() = default;
void foo(int bar) override { foo(bar, 0.0); }
void foo(int bar, double additionalParameter) {cout<<"specialized "<<bar<<" "<<additionalParameter<<endl;}
};
... // elsewhere, for example in main():
SpecializedDerived my_obj;
my_obj.foo(10); // use the override of the base
my_obj.foo(10, 37.2); // use the overload
// suppose that p is a Base* like in the first example
auto *q = dynamic_cast<SpecializedDerived*>(p);
if (q) // dynamic cast makes this nullptr if not the right type
q->foo(10, 37.2);
else cout << "Not specialized"<<endl;
根据一些附加数据用行为覆盖
现在,如果您想在严格的多态上下文中使用您的 foo() 但仍然有一些(隐藏的)附加参数,则有几种可能性,例如:
- 您可以扩展基本签名并添加一个附加的、大部分未使用的具有默认值的参数。在大多数情况下,这是一个坏主意:如果新派生 class 带有另一个参数怎么办。
- 您可以在调用之前注入附加参数(按照您自己的建议在构建时注入,或者在需要时使用 setter 更改值)。适用于大多数情况。唯一的风险是确保在调用
foo()
之前正确设置了附加参数
- 您可以更改签名以将包含所有实际参数的对象用作单个参数。这需要一些额外的开销,但是对于可变参数非常灵活。唯一的问题是这个对象的类型可能也需要是多态的,在这种情况下,您必须确保将正确的参数类型用于正确的对象。在我看来,它超级强大,但超级超级危险。