1) 有没有分布的基类
不。 C++ - 与 Java 或 C# 不同 - 不是 OOP 语言。它是一种多范式语言。 C++ 尽可能地使用元编程,而不是所有东西的基类。例如,并非所有容器都有一个基类。想要接受作为参数容器的函数,没有基类作为参数。相反,您编写一个能够处理任何范围的模板函数。
2) 如果没有,有没有办法创建多态性
是的,当然,但它并不像乍看起来那么简单。
3) 上面的代码是否是正确的方法,什么是错误以及如何解决这个问题。如果有人可以帮助我,我将非常感激
首先,不要在 C++ 中使用显式 new。并且不要使用原始指针来建模所有权。在 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 46
0 0 0 0 0 0 0 1 0 0
5 4 7 6 5 6 6 4 1 8
接下来,我修改了它以使用标准算术类型,并在示例中使用了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 -6 -16 18 32 16
0.683707 0.2806 -1.00301 1.89474 1.19426 0.069856 -0.354233 1.1193 0.0319062 -1.69658
0 0 1 1 0 0 0 0 0 0
3 4 5 5 2 8 3 8 1 5
如果您需要工厂模式,可以在此基础上轻松制作。
如您所见,... 假设... 使分布具有多态性并非易事。而且你需要对你的设计做出一些选择,无论你选择哪一个,你都会做出妥协。
所以你真的需要分析它是否真的值得。只有你能回答这个问题。至少现在你有了一个起点。