在 C++ 中的抽象接口上实现运算符重载
Implementing operator overloading on abstract interface in C++
我正在写一个 class,它是 "mathematical function" 的一个实现。
"mathematical quality function" 可以从一个摘要 class 派生:QualityFunction
。它包含一个 mutable double quality
存储函数一旦求值后的标量值,以及一个 eval
方法用于求值函数值并且必须由每个派生的 class 实现,因为是纯虚方法。
#include <vector>
#include <iostream>
#include <cmath>
using std::cout;
using std::cerr;
using std::endl;
using std::sin;
using std::cos;
using std::vector;
class MathFunction
{
protected:
mutable double quality; // mutable keyword allows to modify quality even in const methods.
virtual void eval(const vector<double> &x) const = 0;
public:
virtual ~MathFunction() {}
double &operator()()
{
return quality;
}
double &operator()(const vector<double> &x) const
{
eval(x);
return quality;
}
};
然后每个从 QualityFunction
派生的 class 都必须实现 eval
方法,因为有很多可能的 QualityFunction
。两个示例是 SumSinFunction 和 SumCosFunction,它们计算参数的 sin
和 cos
的总和:
class SumSinFunction : public MathFunction
{
public:
SumSinFunction(){};
~SumSinFunction() {};
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
quality += sin(x[i]);
}
};
class SumCosFunction : public MathFunction
{
public:
SumCosFunction(){};
~SumCosFunction() {};
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
quality += cos(x[i]);
}
};
需要此层次结构,因为 class Maximizer
接受 MathFunction
个对象,并且重复调用 eval
将找到解决方案 vector<double> x
最大化综合素质。
class Maximizer
{
public:
Maximizer(){}
vector<double> maximize(const MathFunction &f)
{
// do some operations to maximize it
// and return the maximized value
return std::vector<double>();
}
};
现在的问题是当我想通过组合派生对象来创建 MathFunctions 的线性组合以创建一个新对象时,该对象仍然是一个 MathFunction
实例及其成员变量和方法,但它存储了一个质量是其组件质量的线性组合。例如,我想在 MathFunction
上实现 operator+
重载以允许我创建这样的东西:
SumCosFunction cosfun;
SumSinFunction sinfun;
MathFunction m = cosfun + sinfun;
第一个尝试是通过友元函数
重载 operator+
friend MathFunction& operator+(const MathFunction &f1, const MathFunction &f2)
{
MathFunction *f;
// do something
}
但我不能,因为 MathFunction 的构造函数是虚函数!所以问题是,我如何组合从 MathFunction 派生的不同对象来生成一个可以作为 MathFunction 传递给我的 Maximizer
class 的对象?
完整代码可在 coliru http://coliru.stacked-crooked.com/a/3c33664066a3658b
int main()
{
vector<double> x;
for (int i=0; i<10;i++)
x.push_back(i);
SumCosFunction cosfun;
SumSinFunction sinfun;
//MathFunction F;// = cosfun+sinfun;
Maximizer opt;
opt.maximize(cosfun);
return 0;
}
您需要 return 一个 MathFunction
,其 eval
是根据另外两个 MathFunction
的 eval
计算的。您没有执行此操作的 class,因此您需要定义一个。
class SumFunction : public MathFunction
{
MathFunction *one, *two;
public:
SumFunction (MathFunction* one, MathFunction* two)
: one(one), two(two) {}
void eval(const vector<double> &x) const
{
one->eval();
two->eval();
quality = (*one)() + (*two)(); // is this ever correct?
}
};
作为旁注,我认为您的设计将从使 eval
return 成为计算的质量值并丢弃 operator()
和可能的 quality
中受益匪浅。此外,在实际代码中,您可能希望使用 std::shared_ptr
而不是原始指针。
如何实现(示例)pimpl 习语
MathFunctionImpl
将是所有函数的基础
class MathFunctionImpl
{
protected:
mutable double quality; // mutable keyword allows to modify quality even in const methods.
virtual void eval(const vector<double> &x) const = 0;
public:
virtual ~MathFunctionImpl() {}
double &operator()()
{ return quality; }
double &operator()(const vector<double> &x) const
{
eval(x);
return quality;
}
virtual MathFunctionImpl* Clone() const = 0;
};
我们可以使用UnionFunciton
来扩展函数之间的操作:
class UnionFunction : public MathFunctionImpl
{
public:
UnionFunction( MathFunctionImpl* f1, MathFunctionImpl* f2 )
: f1(f1), f2(f2)
{ }
~UnionFunction()
{ delete f1; delete f2; }
protected:
MathFunctionImpl* f1;
MathFunctionImpl* f2;
};
现在,SumSinFunction
和 SumCosFunction
需要一些更改。我添加了控制台消息来测试代码
class SumSinFunction : public MathFunctionImpl
{
public:
SumSinFunction(){}
~SumSinFunction() {}
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
{
if( i>0) std::cout << "+";
std::cout << "sin(" << x[i] << ")";
quality += sin(x[i]);
}
}
MathFunctionImpl* Clone() const
{ return new SumSinFunction; }
};
class SumCosFunction : public MathFunctionImpl
{
public:
SumCosFunction(){}
~SumCosFunction(){}
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
{
if( i>0) std::cout << "+";
std::cout << "cos(" << x[i] << ")";
quality += cos(x[i]);
}
}
MathFunctionImpl* Clone() const
{ return new SumCosFunction; }
};
现在 class 添加到函数:
class SumFunctions : public UnionFunction
{
public:
SumFunctions(MathFunctionImpl* f1, MathFunctionImpl* f2 )
: UnionFunction(f1,f2)
{ }
~SumFunctions()
{ }
void eval(const vector<double> &x) const
{
std::cout << "(";
quality = (*f1)(x);
std::cout << "+";
quality += (*f2)(x);
std::cout << ")";
}
MathFunctionImpl* Clone() const
{ return new SumFunctions(f1->Clone(),f2->Clone()); }
};
好的,我们需要创建一个 class 来存储我们的 pimpl classes:
class MathFunction
{
public:
MathFunction( MathFunctionImpl* impl )
: impl(impl)
{ }
~MathFunction()
{ delete impl; }
double &operator()()
{
return (*impl)();
}
double &operator()(const vector<double> &x) const
{
return (*impl)(x);
}
// This method can be friend
MathFunction operator+(const MathFunction& f2) const
{
return MathFunction(new SumFunctions(impl->Clone(), f2.impl->Clone()));
}
private:
MathFunctionImpl* impl;
};
仅此而已。主要测试代码:
int main()
{
vector<double> x;
for (int i=0; i<10;i++)
x.push_back(i);
MathFunction f1( new SumCosFunction );
MathFunction f2( new SumSinFunction );
MathFunction sum = f1 + f2;
double value = sum(x);
std::cout << "=" << value << std::endl;
return 0;
}
我会选择基于 std::function
的不同设计。
typedef std::function<double (std::vector<double> const&)> MathFunction;
MathFunction sum(MathFunction f1, MathFunction f2)
{
return [f1,f2](std::vector<double> const& x) {
return f1(x) + f2(x);
};
}
您可以使用模板方法或策略设计模式来简化单个函数的定义,或者编写一个使用 std::function<double (double)>
的通用函数,或者使用模板参数。我会坚持你原来的例子。
class SumCosFunction
{
public:
double operator()(const vector<double> &x) const
{
if (!quality) {
*quality = 0;
for (size_t i=0; i<x.size(); ++i)
*quality += cos(x[i]);
}
return *quality;
}
private:
mutable std::experimental::optional<double> quality;
};
class SumSinFunction
{
public:
double operator()(const vector<double> &x) const
{
if (!quality) {
*quality = 0;
for (size_t i=0; i<x.size(); ++i)
*quality += sin(x[i]);
}
return *quality;
}
private:
mutable std::experimental::optional<double> quality;
};
Combining both functions is simple:
auto consSin = sum( SumCosFunction{}, SumSinFunction{} );
我正在写一个 class,它是 "mathematical function" 的一个实现。
"mathematical quality function" 可以从一个摘要 class 派生:QualityFunction
。它包含一个 mutable double quality
存储函数一旦求值后的标量值,以及一个 eval
方法用于求值函数值并且必须由每个派生的 class 实现,因为是纯虚方法。
#include <vector>
#include <iostream>
#include <cmath>
using std::cout;
using std::cerr;
using std::endl;
using std::sin;
using std::cos;
using std::vector;
class MathFunction
{
protected:
mutable double quality; // mutable keyword allows to modify quality even in const methods.
virtual void eval(const vector<double> &x) const = 0;
public:
virtual ~MathFunction() {}
double &operator()()
{
return quality;
}
double &operator()(const vector<double> &x) const
{
eval(x);
return quality;
}
};
然后每个从 QualityFunction
派生的 class 都必须实现 eval
方法,因为有很多可能的 QualityFunction
。两个示例是 SumSinFunction 和 SumCosFunction,它们计算参数的 sin
和 cos
的总和:
class SumSinFunction : public MathFunction
{
public:
SumSinFunction(){};
~SumSinFunction() {};
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
quality += sin(x[i]);
}
};
class SumCosFunction : public MathFunction
{
public:
SumCosFunction(){};
~SumCosFunction() {};
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
quality += cos(x[i]);
}
};
需要此层次结构,因为 class Maximizer
接受 MathFunction
个对象,并且重复调用 eval
将找到解决方案 vector<double> x
最大化综合素质。
class Maximizer
{
public:
Maximizer(){}
vector<double> maximize(const MathFunction &f)
{
// do some operations to maximize it
// and return the maximized value
return std::vector<double>();
}
};
现在的问题是当我想通过组合派生对象来创建 MathFunctions 的线性组合以创建一个新对象时,该对象仍然是一个 MathFunction
实例及其成员变量和方法,但它存储了一个质量是其组件质量的线性组合。例如,我想在 MathFunction
上实现 operator+
重载以允许我创建这样的东西:
SumCosFunction cosfun;
SumSinFunction sinfun;
MathFunction m = cosfun + sinfun;
第一个尝试是通过友元函数
重载operator+
friend MathFunction& operator+(const MathFunction &f1, const MathFunction &f2)
{
MathFunction *f;
// do something
}
但我不能,因为 MathFunction 的构造函数是虚函数!所以问题是,我如何组合从 MathFunction 派生的不同对象来生成一个可以作为 MathFunction 传递给我的 Maximizer
class 的对象?
完整代码可在 coliru http://coliru.stacked-crooked.com/a/3c33664066a3658b
int main()
{
vector<double> x;
for (int i=0; i<10;i++)
x.push_back(i);
SumCosFunction cosfun;
SumSinFunction sinfun;
//MathFunction F;// = cosfun+sinfun;
Maximizer opt;
opt.maximize(cosfun);
return 0;
}
您需要 return 一个 MathFunction
,其 eval
是根据另外两个 MathFunction
的 eval
计算的。您没有执行此操作的 class,因此您需要定义一个。
class SumFunction : public MathFunction
{
MathFunction *one, *two;
public:
SumFunction (MathFunction* one, MathFunction* two)
: one(one), two(two) {}
void eval(const vector<double> &x) const
{
one->eval();
two->eval();
quality = (*one)() + (*two)(); // is this ever correct?
}
};
作为旁注,我认为您的设计将从使 eval
return 成为计算的质量值并丢弃 operator()
和可能的 quality
中受益匪浅。此外,在实际代码中,您可能希望使用 std::shared_ptr
而不是原始指针。
如何实现(示例)pimpl 习语
MathFunctionImpl
将是所有函数的基础
class MathFunctionImpl
{
protected:
mutable double quality; // mutable keyword allows to modify quality even in const methods.
virtual void eval(const vector<double> &x) const = 0;
public:
virtual ~MathFunctionImpl() {}
double &operator()()
{ return quality; }
double &operator()(const vector<double> &x) const
{
eval(x);
return quality;
}
virtual MathFunctionImpl* Clone() const = 0;
};
我们可以使用UnionFunciton
来扩展函数之间的操作:
class UnionFunction : public MathFunctionImpl
{
public:
UnionFunction( MathFunctionImpl* f1, MathFunctionImpl* f2 )
: f1(f1), f2(f2)
{ }
~UnionFunction()
{ delete f1; delete f2; }
protected:
MathFunctionImpl* f1;
MathFunctionImpl* f2;
};
现在,SumSinFunction
和 SumCosFunction
需要一些更改。我添加了控制台消息来测试代码
class SumSinFunction : public MathFunctionImpl
{
public:
SumSinFunction(){}
~SumSinFunction() {}
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
{
if( i>0) std::cout << "+";
std::cout << "sin(" << x[i] << ")";
quality += sin(x[i]);
}
}
MathFunctionImpl* Clone() const
{ return new SumSinFunction; }
};
class SumCosFunction : public MathFunctionImpl
{
public:
SumCosFunction(){}
~SumCosFunction(){}
protected:
void eval(const vector<double> &x) const
{
quality = 0;
for (size_t i=0; i<x.size(); ++i)
{
if( i>0) std::cout << "+";
std::cout << "cos(" << x[i] << ")";
quality += cos(x[i]);
}
}
MathFunctionImpl* Clone() const
{ return new SumCosFunction; }
};
现在 class 添加到函数:
class SumFunctions : public UnionFunction
{
public:
SumFunctions(MathFunctionImpl* f1, MathFunctionImpl* f2 )
: UnionFunction(f1,f2)
{ }
~SumFunctions()
{ }
void eval(const vector<double> &x) const
{
std::cout << "(";
quality = (*f1)(x);
std::cout << "+";
quality += (*f2)(x);
std::cout << ")";
}
MathFunctionImpl* Clone() const
{ return new SumFunctions(f1->Clone(),f2->Clone()); }
};
好的,我们需要创建一个 class 来存储我们的 pimpl classes:
class MathFunction
{
public:
MathFunction( MathFunctionImpl* impl )
: impl(impl)
{ }
~MathFunction()
{ delete impl; }
double &operator()()
{
return (*impl)();
}
double &operator()(const vector<double> &x) const
{
return (*impl)(x);
}
// This method can be friend
MathFunction operator+(const MathFunction& f2) const
{
return MathFunction(new SumFunctions(impl->Clone(), f2.impl->Clone()));
}
private:
MathFunctionImpl* impl;
};
仅此而已。主要测试代码:
int main()
{
vector<double> x;
for (int i=0; i<10;i++)
x.push_back(i);
MathFunction f1( new SumCosFunction );
MathFunction f2( new SumSinFunction );
MathFunction sum = f1 + f2;
double value = sum(x);
std::cout << "=" << value << std::endl;
return 0;
}
我会选择基于 std::function
的不同设计。
typedef std::function<double (std::vector<double> const&)> MathFunction;
MathFunction sum(MathFunction f1, MathFunction f2)
{
return [f1,f2](std::vector<double> const& x) {
return f1(x) + f2(x);
};
}
您可以使用模板方法或策略设计模式来简化单个函数的定义,或者编写一个使用 std::function<double (double)>
的通用函数,或者使用模板参数。我会坚持你原来的例子。
class SumCosFunction
{
public:
double operator()(const vector<double> &x) const
{
if (!quality) {
*quality = 0;
for (size_t i=0; i<x.size(); ++i)
*quality += cos(x[i]);
}
return *quality;
}
private:
mutable std::experimental::optional<double> quality;
};
class SumSinFunction
{
public:
double operator()(const vector<double> &x) const
{
if (!quality) {
*quality = 0;
for (size_t i=0; i<x.size(); ++i)
*quality += sin(x[i]);
}
return *quality;
}
private:
mutable std::experimental::optional<double> quality;
};
Combining both functions is simple:
auto consSin = sum( SumCosFunction{}, SumSinFunction{} );