Slu*_*ger 3 c++ oop inheritance
对于C++类,我想设计一个类层次结构,处理二进制运算+= 和-=.所需的层次结构(每个问题要求)描述如下.我们有两个类Addition和Subtraction.这些是类的基类Binops.然后一个类Operations继承自Binops.所以图表看起来像这样
Operations
|
?
Binops
| |
| |
+---+ +---+
? ?
Addition Subtraction
Run Code Online (Sandbox Code Playgroud)
这Binops是朋友班Operations.需要以下成员函数:Operations实现私有函数
void add(Operations const &rhs);
void sub(Operations const &rhs);
Run Code Online (Sandbox Code Playgroud)
而且这个课Addition需要实施
Addition::operator+=(Operations const &rhs)
Run Code Online (Sandbox Code Playgroud)
同样的Subtraction课程.
我对这个设计的实现以及它背后的想法都有疑问.
我看到它的样子,一旦这一框架已经准备好,例如像其他类Matrix类可以从继承Operations类,然后我们做Matrix的一个朋友Operations这样Matrix可以使用+=等等.然后,我们也只会实现add在功能Operations和+=操作将然后为Matrix全班工作.但后来我们为什么不干脆直接在+=运营商Operations,甚至Matrix?也许这个想法是,我们还可以定义=在操作Addition使用add功能Operations,使实施后add,无论是+=和+工作一气呵成.
从实现的角度来看:应该是什么的返回类型+=的Addition?我相信它应该是Operations,但是Addition类标题应该包含Operations导致循环依赖的标头.
此外,Addition能够使用add从Operations,是有一些方法,我们可以做,没有做Addition的朋友Operations吗?我不认为只是交Addition朋友Binops就足够了,因为友谊不是传递性的.
很抱歉这个问题很长.提前感谢任何见解!
似乎那些类名有点偏.我的心灵解码是,Addition是HasAddition.所以我们HasOperations继承自HasBinOps,继承自HasAddition和HasSubtraction.
所以我得到了基本的计划.但我要回答如何做到这一点.这可能与你的任务不符,但老实说你的任务不是我的!
我们不希望所有基本操作都进行虚拟运行时调度和动态分配.我们想要静态多态,而不是动态多态.
幸运的是,在C++中我们有静态多态.实现它的一种典型方法是通过CRTP - 奇怪的重复模板模式.
我们不必在这里使用CRTP.我们可以依靠Koenig查找!
Koenig查找的事实是,在确定operator+要调用的内容时,friend会考虑父类.我们通过使派生类型成为内部来注入friend operator+匹配的派生类型.templatehas_addition
当我们拥有我们的时候matrix:has_addition,我们就会召唤+.找到此模板.然后我们替换参数的类型 - 完整类型,而不是has_addition父类型.
在这个完整类型中,我们有一个.add方法.
所以我们可以从一个类型继承,使得该operator+类型具有基于我们从中派生的类型的不同实现,但是这个调度在编译时静态完成.
在运行时,has_addition基本上消失了.相反,我们只是得到了一堆+路由.add.
所以,不用多说,这里是has_addition:
struct has_addition {
// implement + in terms of += on the lhs:
template<class L, class R>
friend std::decay_t<L> operator+( L&& lhs, R&& rhs ) {
if (!std::is_reference<L>{}) { // rvalue lhs
return std::forward<L>(lhs) += rhs;
} else if (!std::is_reference<R>{}) { // rvalue rhs
return std::forward<R>(rhs) += lhs; // assumes + commutes
} else { // rvalue neither
auto tmp = std::forward<L>(lhs);
return tmp += rhs;
}
}
// notice += on an rvalue returns a copy.
// This permits reference lifetime extension:
template<class L, class R>
friend L operator+=( L&& lhs, R&& rhs ) {
lhs.add( std::forward<R>(rhs) );
return std::forward<L>(lhs);
}
};
Run Code Online (Sandbox Code Playgroud)
你通过以下方式使用它
struct bob : has_addition {
int x = 0;
void add( bob const& rhs ) {
x += rhs.x;
}
};
Run Code Online (Sandbox Code Playgroud)
实例.
现在,这两个+并+=会为您根据您的实现add方法.更重要的是,它们有多个rvalue和lvalue重载.如果实现move-construct,则会获得自动性能提升.如果实现在右侧采用右值的add,则会获得自动性能提升.
如果你没有写rvalue重载add和move-construct,那么事情仍然有效.我们将这些因素(添加您可以丢弃的内容,回收存储,以及如何+工作的微优化)相互分离.结果更容易编写内置的一堆微优化代码.
现在,大多数微优化has_addition::operator+都不是第一次通过所必需的.
struct has_addition {
// implement + in terms of += on the lhs:
template<class L, class R>
friend L operator+( L lhs, R&& rhs ) {
return std::move(lhs) += std::forward<R>(rhs);
}
template<class L, class R>
friend L operator+=( L&& lhs, R&& rhs ) {
lhs.add( std::forward<R>(rhs) );
return std::forward<L>(lhs);
}
};
Run Code Online (Sandbox Code Playgroud)
这是更清洁和近乎最佳.
然后,我们将其扩展
struct has_subtraction; // implement
struct has_binops:
has_subtraction,
has_addition
{};
struct has_operations:
has_binops
{};
Run Code Online (Sandbox Code Playgroud)
但实际上,很少有类型有各种类型的操作,所以我个人不喜欢这样.
你可以使用SFINAE(替换故障是不是一个错误),以检测是否add,subtact,multiply,divide,order,equals等你的类型来实现,写maybe_has_addition<D>,做一个SFINAE测试D,以确定其是否已D.add( D const& )执行.当且仅当如此has_addition继承自maybe_has_addition<D>.
然后你可以设置它,以便通过执行以下操作来编写无数的运算符重载:
struct matrix: maybe_has_operations<matrix>
Run Code Online (Sandbox Code Playgroud)
在您实施新操作的地方matrix,越来越多的重载运营商开始工作.
然而,这是一个不同的问题.
使用动态多态(虚函数)执行此操作是一团糟.实际上,您是否想要在编写时跳过多个vtable,动态分配并丢失所有编译时类型的安全性matrix1 = matrix2 + matrix3?这不是Java.
这位朋友很容易.请注意如何has_addition调用D.add(D const&).我们可以add在内部私有化D,但只有我们friend struct has_addition;在体内D.
所以has_addition既是父母,D也是朋友D.
我自己,我只是add暴露,因为它是无害的.
这种技术有缺点,就像你添加两个不同的类时所发生的那样has_addition.
您可以在boost.operators中看到更加充实的版本,它也使用相关技术.