首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++11多态性与分布

C++11多态性与分布
EN

Stack Overflow用户
提问于 2019-09-25 18:50:23
回答 2查看 339关注 0票数 0

我有一个问题,我想要一个对象来表示c++11中随机库中使用的任何分布类(多态性)。因为据我所知,分发没有一个公共基类,所以我正在使用工厂模式来解决我的问题。有没有更好的方法来做到这一点?我正在添加下面的代码,其中有一些错误

代码语言:javascript
复制
#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)以上代码是否正确,错误是什么,如何解决。如果有人能在这件事上帮我,我将非常感激。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-09-25 21:09:55

1)是否有分发的基类?

不是的。C++与Java或C#不同,它不是一种面向对象的语言。这是一种多范式的语言。C++在任何可能的情况下都会使用元编程,而不是任何东西的基类。例如,并不是所有容器都有一个基类。要接受作为参数容器的函数,不要使用基类作为参数。相反,您编写的模板函数能够处理任何类型的范围。

(

2)如果没有,有没有办法创建多态性?

是的,当然,但这并不像乍一看上去那么简单。

3)上面的代码是否正确,错误是什么,以及如何解决这个问题。如果有人能在这件事上帮我,我将非常感激。

首先,不要在new中使用显式C++。不要使用原始指针来创建模型所有权。在C++中,我们使用RAII原则,因此,当需要拥有资源的指针时,我们使用智能指针。

第二,不要使用void*。如果您确实需要类型擦除,请使用std::any,虽然目前使用起来很麻烦,但它提供了类型安全类型擦除。

因此,我没有尝试修复编译问题--这不会修复您的设计,而是创建了一个多态性的实现版本。

当我们开始这个任务时,我们遇到的第一个问题是:分发生成器函数(operator())中的不同类型。一次,不同的发行版可以得到不同的类型(intfloat等)。然后,我们需要一个生成器--这是模板--来调用它。这使得我们不可能简单地创建一个我们可以覆盖的虚拟函数。

为了返回不同的类型,您需要std::variant。由于实现已经很困难,我将在第二个示例中这样做。

对于发电机来说,有一些方法可以避免使用简单的设计。我选择在派生发行版中存储指向生成器的指针。这编码了发行版中生成器的类型,但在许多情况下都是可以接受的。另一种更长的方法是创建生成器的多态版本。

这是第一个处理多态int分布向量的例子。在示例中,向量存储均匀、二项式和泊松分布,以查看多态性的作用:

代码语言:javascript
复制
#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>>;
代码语言:javascript
复制
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 longdoubleint,以及前面所有的不同发行版:

代码语言:javascript
复制
#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>>;
代码语言:javascript
复制
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

如果您需要一个工厂模式,这可以很容易地在此基础上。

如你所见,它是..。比方说..。使发行版具有多态性不是件容易的事。你需要对你的设计做出一些选择,无论你选择哪一个,你都要做出妥协。

所以你真的需要分析它是否值得。只有你才能回答这个问题。至少现在你有了一个起点。

票数 1
EN

Stack Overflow用户

发布于 2019-09-25 19:55:42

如果您知道哪个发行版适用于哪个场景,那么使用模板是更好的方法。下面是相同的示例代码:

代码语言:javascript
复制
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;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58104845

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档