我有一个问题,我想要一个对象来表示c++11中随机库中使用的任何分布类(多态性)。因为据我所知,分发没有一个公共基类,所以我正在使用工厂模式来解决我的问题。有没有更好的方法来做到这一点?我正在添加下面的代码,其中有一些错误
#include <iostream>
#include<random>
using namespace std;
class Distribution
{
public:
void *distribution;
virtual void get_distribution() = 0;
};
class normal_dist: public Distribution
{
public:
//normal_distribution<double> *distribution;
void get_distribution()
{
distribution = new normal_distribution<double>(5.0,2.0);
}
};
class uniform_real_dist: public Distribution
{
public:
//uniform_real_distribution<double> *distribution;
void get_distribution()
{
distribution = new uniform_real_distribution<double>(0.0,1.0);
}
};
class Helper
{
public:
Distribution *dist;
default_random_engine generator;
Helper(string dist_type)
{
if(dist_type == "normal")
{
dist = new normal_dist;
dist->get_distribution();
}
}
};
int main() {
Helper *help = new Helper("normal");
cout<<(*(help->dist->distribution))(help->generator);
// your code goes here
return 0;
}我的问题有三重
( 1)是否有分发的基类?
2)如果没有,是否有办法创建多态性?
( 3)以上代码是否正确,错误是什么,如何解决。如果有人能在这件事上帮我,我将非常感激。
发布于 2019-09-25 21:09:55
1)是否有分发的基类?
不是的。C++与Java或C#不同,它不是一种面向对象的语言。这是一种多范式的语言。C++在任何可能的情况下都会使用元编程,而不是任何东西的基类。例如,并不是所有容器都有一个基类。要接受作为参数容器的函数,不要使用基类作为参数。相反,您编写的模板函数能够处理任何类型的范围。
(
2)如果没有,有没有办法创建多态性?
是的,当然,但这并不像乍一看上去那么简单。
3)上面的代码是否正确,错误是什么,以及如何解决这个问题。如果有人能在这件事上帮我,我将非常感激。
首先,不要在new中使用显式C++。不要使用原始指针来创建模型所有权。在C++中,我们使用RAII原则,因此,当需要拥有资源的指针时,我们使用智能指针。
第二,不要使用void*。如果您确实需要类型擦除,请使用std::any,虽然目前使用起来很麻烦,但它提供了类型安全类型擦除。
因此,我没有尝试修复编译问题--这不会修复您的设计,而是创建了一个多态性的实现版本。
当我们开始这个任务时,我们遇到的第一个问题是:分发生成器函数(operator())中的不同类型。一次,不同的发行版可以得到不同的类型(int、float等)。然后,我们需要一个生成器--这是模板--来调用它。这使得我们不可能简单地创建一个我们可以覆盖的虚拟函数。
为了返回不同的类型,您需要std::variant。由于实现已经很困难,我将在第二个示例中这样做。
对于发电机来说,有一些方法可以避免使用简单的设计。我选择在派生发行版中存储指向生成器的指针。这编码了发行版中生成器的类型,但在许多情况下都是可以接受的。另一种更长的方法是创建生成器的多态版本。
这是第一个处理多态int分布向量的例子。在示例中,向量存储均匀、二项式和泊松分布,以查看多态性的作用:
#include <iostream>
#include <random>
#include <memory>
#include <vector>
class Base_int_distribution
{
public:
virtual int generate() = 0;
virtual ~Base_int_distribution() = default;
};
template <class Eng, class D>
class Int_distribution : public Base_int_distribution
{
std::shared_ptr<Eng> engine_;
D distribution_;
public:
template <class... Args>
Int_distribution(std::shared_ptr<Eng> engine, Args... args)
: engine_{std::move(engine)},
distribution_{args...} {}
Int_distribution(const Int_distribution&) = default;
Int_distribution(Int_distribution&&) = default;
Int_distribution& operator=(const Int_distribution&) = default;
Int_distribution& operator=(Int_distribution&&) = default;
int generate() override { return distribution_(*engine_); }
};
// some alias helpers, because oh boy, does it get crazy with the types
template <class Gen>
using Uniform_int_dist_poly = Int_distribution<Gen, std::uniform_int_distribution<int>>;
template <class Gen>
using Binomial_int_dist_poly = Int_distribution<Gen, std::binomial_distribution<int>>;
template <class Gen>
using Poisson_int_dist_poly = Int_distribution<Gen, std::poisson_distribution<int>>;int main()
{
std::random_device rd;
auto eng = std::make_shared<std::mt19937>(rd());
std::vector<std::unique_ptr<Base_int_distribution>> distributions{};
// nope, we can't use initializer list, don't get me started
distributions.push_back(std::make_unique<Uniform_int_dist_poly<std::mt19937>>(eng, -50, 50));
distributions.push_back(std::make_unique<Binomial_int_dist_poly<std::mt19937>>(eng, 4, .05));
distributions.push_back(std::make_unique<Poisson_int_dist_poly<std::mt19937>>(eng, 4.));
// for each distribution
for (auto& dist : distributions)
{
// get 10 numbers
for (int i = 0; i < 10; ++i)
{
std::cout << dist->generate() << " ";
}
std::cout << std::endl;
}
return 0;
}样本输出:
21 0 43 2 -37 38 39 42 -37 0 0 0
接下来,我修改了它以处理标准的算术类型,并在示例中使用了long long、double和int,以及前面所有的不同发行版:
#include <iostream>
#include <random>
#include <memory>
#include <vector>
#include <variant>
using DistTs = std::variant<
signed char, unsigned char, char,
short, int, long, long long,
unsigned short, unsigned int, unsigned long, unsigned long long,
float, double, long double>;
class Base_distribution
{
public:
virtual DistTs generate() = 0;
virtual ~Base_distribution() = default;
};
template <class Eng, class D>
class Distribution : public Base_distribution
{
std::shared_ptr<Eng> engine_;
D distribution_;
public:
template <class... Args>
Distribution(std::shared_ptr<Eng> engine, Args... args)
: engine_{std::move(engine)},
distribution_{args...} {}
Distribution(const Distribution&) = default;
Distribution(Distribution&&) = default;
Distribution& operator=(const Distribution&) = default;
Distribution& operator=(Distribution&&) = default;
DistTs generate() override { return distribution_(*engine_); }
};
// some alias helpers, because oh boy, does it get crazy with the types
template <class Gen, class T = int>
using Uniform_int_dist_poly = Distribution<Gen, std::uniform_int_distribution<T>>;
template <class Gen, class T = double>
using Uniform_real_dist_poly = Distribution<Gen, std::uniform_real_distribution<T>>;
template <class Gen, class T = int>
using Binomial_dist_poly = Distribution<Gen, std::binomial_distribution<T>>;
template <class Gen, class T = int>
using Poisson_int_dist_poly = Distribution<Gen, std::poisson_distribution<T>>;int main()
{
std::random_device rd;
auto eng = std::make_shared<std::mt19937>(rd());
std::vector<std::unique_ptr<Base_distribution>> distributions{};
// nope, we can't use initializer list, don't get me started
distributions.push_back(std::make_unique<Uniform_int_dist_poly<std::mt19937, long long>>(eng, -50, 50));
distributions.push_back(std::make_unique<Uniform_real_dist_poly<std::mt19937, double>>(eng, -2.0, 2.0));
distributions.push_back(std::make_unique<Binomial_dist_poly<std::mt19937>>(eng, 4, .05));
distributions.push_back(std::make_unique<Poisson_int_dist_poly<std::mt19937>>(eng, 4.));
// for each distribution
for (auto& dist : distributions)
{
// get 10 numbers
for (int i = 0; i < 10; ++i)
{
std::visit([](auto e) { std::cout << e << " "; }, dist->generate());
}
std::cout << std::endl;
}
return 0;
}样本输出:
8 -25 29 14 -17 -17 -16 18 32 16 0.2806 -1.00301 1.89474 1.19426 0.069856 -0.354233 0.0319062 -1.69658 0 1 0 0 0 3 4 5 5 2 8 3 8 1 5
如果您需要一个工厂模式,这可以很容易地在此基础上。
如你所见,它是..。比方说..。使发行版具有多态性不是件容易的事。你需要对你的设计做出一些选择,无论你选择哪一个,你都要做出妥协。
所以你真的需要分析它是否值得。只有你才能回答这个问题。至少现在你有了一个起点。
发布于 2019-09-25 19:55:42
如果您知道哪个发行版适用于哪个场景,那么使用模板是更好的方法。下面是相同的示例代码:
template<class C, template <class> class M>
M<C> getDistributionObject(const C& arg1, const C& arg2)
{
return M<C>(arg1,arg2);
}
int main() {
auto normalDistribution = getDistributionObject<double,normal_distribution>(5.0,2.0);
return 0;
}https://stackoverflow.com/questions/58104845
复制相似问题