我有三层类,例如BaseSpreading(基类)、IterativeMapSpreading(BaseSpreading 的子类之一)和TentSpreading、BernoulliSpreading(IterativeMapSpreading 的两个子类)。一个名为generateSpreading的方法出现在所有类中。用户定义的选项指定传播,例如“帐篷”或“伯努利”。然后 if-else 控制结构调用相应子类的 generateSpreading 方法(“tent”和“bernoulli”将分别调用 TentSpreading 和 BernoulliSpreading 子类的 generateSpreading 方法)。
我知道如何通过将 generateSpreading 声明为虚拟并定义一个基类指针(指向 BaseSpreading)来解决我的问题。然后,派生类对象可以根据用户选项在if-else控制结构内实例化。尽管这样的实例在控制结构范围之外是不可用的,但是可以使用工厂模式将基类指针指向派生类对象,例如参见这篇文章。尽管基于虚拟方法的解决方案有效,但由于其性能不佳,它对我的目的没有用。我的模拟调用了数百万次虚函数。
作为替代方法,我使用了模板编程。在这种情况下,我必须处理嵌套模板类的实例在 if-else 结构范围之外不可用的问题。我的问题是在使用模板类时是否可以扩展工厂模式的想法。为了使这项工作,如果可能的话,我必须创建一个指向模板基类的指针,然后将它指向实例化的模板子类,该子类在 if-else 结构中定义。然后问题归结为为模板基类定义一个默认模板参数,这一直是我的困难。这个链接给出了一个如何定义默认模板参数的示例,该参数不是众所周知的类型,例如“int”(链接示例的默认参数是向量的标准“分配器”模板)。
这些是我的脚本:
base_spreading.h:
#ifndef BASE_SPREADING_H_
#define BASE_SPREADING_H_
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/numeric/ublas/vector.hpp>
template <class S>
class BaseSpreading
{
public:
BaseSpreading(S& spreading);
void generateSpreading(boost::numeric::ublas::vector<double>&);
private:
S& spreading_;
};
template <class S>
BaseSpreading<S>::BaseSpreading(S& spreading) : spreading_(spreading) {}
template <class S>
void BaseSpreading<S>::generateSpreading(
boost::numeric::ublas::vector<double>& spr) {
spreading_.generateSpreading(spr);
}
#endif /* BASE_SPREADING_H_ */
spread_iterative_map.h
#ifndef SPREADING_ITERATIVE_MAP_H_
#define SPREADING_ITERATIVE_MAP_H_
#include <boost/numeric/ublas/vector.hpp>
template <class S>
class IterativeMapSpreading
{
public:
IterativeMapSpreading(S& spreading);
double evaluateMap(double);
void generateSpreading(boost::numeric::ublas::vector<double>&);
double sampleInitialPoint();
private:
S& spreading_;
void calculateFollowingPoints(boost::numeric::ublas::vector<double>&);
};
template <class S>
IterativeMapSpreading<S>::IterativeMapSpreading(S& spreading) :
spreading_(spreading) {}
template <class S>
void IterativeMapSpreading<S>::calculateFollowingPoints(
boost::numeric::ublas::vector<double>& spr) {
for (unsigned int i=1; i<spr.size(); ++i) {
spr(i) = spreading_.evaluateMap(spr(i-1));
}
}
template <class S>
double IterativeMapSpreading<S>::evaluateMap(double x) {
return spreading_.evaluateMap(x);
}
template <class S>
void IterativeMapSpreading<S>::generateSpreading(
boost::numeric::ublas::vector<double>& spr) {
spr(0) = spreading_.sampleInitialPoint();
calculateFollowingPoints(spr);
}
template <class S>
double IterativeMapSpreading<S>::sampleInitialPoint() {
return spreading_.sampleInitialPoint();
}
#endif /* SPREADING_ITERATIVE_MAP_H_ */
传播_帐篷.h:
#ifndef SPREADING_TENT_H_
#define SPREADING_TENT_H_
#include <math.h>
#include "random_number_generation.h"
class TentSpreading
{
public:
TentSpreading(double uniformMin=0, double uniformMax=1,
double nonCentrality=0.5);
double evaluateMap(double);
double sampleInitialPoint();
private:
const double uniformMin_, uniformMax_, nonCentrality_;
double leftIntercept_, leftSlope_, rightIntercept_, rightSlope_;
boost::random::uniform_real_distribution<> Uniform;
void setLines();
void validateParameters() const;
};
#endif /* SPREADING_TENT_H_ */
spread_tent.cpp:
#include "spreading_tent.h"
TentSpreading::TentSpreading(double uniformMin, double uniformMax,
double nonCentrality) : uniformMin_(uniformMin), uniformMax_(uniformMax),
nonCentrality_(nonCentrality), Uniform(uniformMin, uniformMax) {
setLines();
}
double TentSpreading::evaluateMap(double x) {
double y;
if((uniformMin_<=x) && (x<nonCentrality_))
y = leftSlope_*x+leftIntercept_;
else if((nonCentrality_<=x) && (x<=uniformMax_))
y = rightSlope_*x+rightIntercept_;
return y;
}
double TentSpreading::sampleInitialPoint() {
return Uniform(rng);
}
void TentSpreading::setLines() {
leftSlope_ = (uniformMax_-uniformMin_)/(nonCentrality_-uniformMin_);
leftIntercept_ = -uniformMin_*(uniformMax_-nonCentrality_)/
(nonCentrality_-uniformMin_);
rightSlope_ = -(uniformMax_-uniformMin_)/(uniformMax_-nonCentrality_);
rightIntercept_ = (pow(uniformMax_, 2)-uniformMin_*nonCentrality_)/
(uniformMax_-nonCentrality_);
}
最后,main.cpp 中感兴趣的部分:
try {
if (sbcOptions.mode=="sim-spr") {
boost::numeric::ublas::vector<double> sprVector(3);
if (sbcOptions.spr=="tent") {
TentSpreading tent(-1, 1, 0);
IterativeMapSpreading<TentSpreading> map(tent);
BaseSpreading<IterativeMapSpreading<TentSpreading> > spreading(map);
}
spreading.generateSpreading(sprVector);
}
}
catch(std::logic_error& logicError) {
logTee << logicError.what() << "\n";
return 1;
}
为了使用工厂模式,我尝试添加一个抽象基类,称为 FactoryBaseSpreading,BaseSpreading 是其子类。这是 FactoryBaseSpreading 的定义:
FactoryBaseSpreading {
public:
static BaseSpreading* create(std::string type);
}
FactoryBaseSpreading* FactoryBaseSpreading::create(std::string type) {
if (type == "tent") {
TentSpreading tent(-1, 1, 0);
IterativeMapSpreading<TentSpreading> map(tent);
return new BaseSpreading<IterativeMapSpreading<TentSpreading> >(map);
}
return NULL;
}
//Also, change the following line in base_spreading.h:
class BaseSpreading : public FactoryBaseSpreading
这样,我可以在“main.cpp”中定义一个指向 FactoryBaseSpreading 的指针,并将其指向 if-else 结构内的适当子类,例如:
FactoryBaseSpreading* spreading;
try {
if (sbcOptions.mode=="sim-spr") {
boost::numeric::ublas::vector<double> sprVector(3);
if (sbcOptions.spr=="tent") {
spreading = FactoryBaseSpreading::create("tent");
}
spreading.generateSpreading(sprVector);
}
}
catch(std::logic_error& logicError) {
logTee << logicError.what() << "\n";
return 1;
}
如果我在 FactoryBaseSpreading 中定义了一个虚拟 generateSpreading 方法,则编译并运行正常。但是,我试图避免的是使用虚函数,所以工厂模式并不能解决我的问题。
于是,我想到了在 BaseSpreading 基类中定义“create”方法,而不创建 FactoryBaseSpreading 抽象基类。在这种情况下,“create”方法的定义变为:
template <class S>
BaseSpreading<S>* BaseSpreading<S>::create(std::string type) {
if (type == "tent") {
TentSpreading tent(-1, 1, 0);
IterativeMapSpreading<TentSpreading> map(tent);
return new BaseSpreading<IterativeMapSpreading<TentSpreading> >(map);
}
return NULL;
}
这段代码的问题是我不能再定义基类指针,因为它依赖于模板参数 S。例如,这是无效的:
BaseSpreading<S>* spreading;
这就是为什么我要问是否可以在 BaseSpreading 的定义中提供默认参数。即使我能够指定默认模板参数,我也无法在运行时切换模板参数,因为模板是在编译时由编译器实例化的,如此处所述。所以,我似乎无法避免使用虚函数,甚至在模板元编程的帮助下也是如此。我能想到的解决问题的唯一其他方法是通过函数指针、委派函数,或者使用函数式编程完全改变我的代码设计......