2

I have a class that initializes a boost::normal_distribution object in the constructor. How do I store this object in a member so that it is available elsewhere in the class? I think I want to store a pointer to the boost object, but once I leave the constructor, the object gets released from the stack. So, I think I really want to allocate the normal distribution object on the heap with a new, but I'm at a loss to get the syntax right.

class Generator 
{
  private:
    boost::variate_generator<boost::mt19937&, 
                             boost::normal_distribution<> > *_var_nor;

  public:
    Generator( int avg_size, int stddev_size )

      PhraseGenerator( size, words );

      boost::mt19937 rng; // I don't seed it on purpouse (it's not relevant)
      boost::normal_distribution<> nd(avg_size, stddev_size);
      boost::variate_generator<boost::mt19937&, 
                               boost::normal_distribution<> > var_nor(rng, nd);

            _var_nor = &var_nor;
    };

    int normal_distrib_value()
    {
        return (*_var_nor)();
    }
};
Praetorian
  • 106,671
  • 19
  • 240
  • 328
Mercutio
  • 23
  • 1
  • 4

2 Answers2

0

If you want all your objects of type Generator to share the same member, you should declare it static.

Example:

// in Generator.h

class Generator 
{
  private:
    static boost::variate_generator<boost::mt19937, 
                             boost::normal_distribution<> > _var_nor;
  // ...
};

// in Generator.cpp
#include "Generator.h"

boost::variate_generator<boost::mt19937, 
   boost::normal_distribution<> > Generator::_var_nor{
       boost::mt19937{}, 
       boost::normal_distribution<>{}
};
// ...
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
0

In your example, once the constructor exits, _var_nor will point to an object that no longer exists, and any attempt to dereference it later will be undefined behavior. You could new the normal_distribution, but that's not the only thing you'd need to new. Notice that the first template parameter for the variate_generator is boost::mt19937&, so it's only storing a reference to the Mersenne Twister, not copying it, so you have an identical problem with that too. You could change the parameter type to a non-reference, or new the mt19937 as well.

Anyway, there's a simpler solution, and it doesn't involve pointers or new. Just make all 3, mt19937, normal_distribution and variate_generator, members of your class. Remember, the order you declare them is important since that's the order they'll be initialized in.

class Generator 
{
  private:
    boost::mt19937 _rng;
    boost::normal_distribution<> _nd;
    boost::variate_generator<boost::mt19937&, 
                             boost::normal_distribution<> > _var_nor;

  public:
    Generator( int avg_size, int stddev_size )
    : _nd(avg_size, stddev_size)
    , _var_nor(_rng, _nd)
    {
        PhraseGenerator( size, words );
    }

    int normal_distrib_value()
    {
        return _var_nor();
    }
};
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Thanks for the detailed response, Praetorian. That worked. About the revised constructor... I presume the part ... " : _nd(avg_size, stddev_size), _var_nor(_rng, _nd) " is calling the constructors for the _nd and _var_nor objects... assuming that is correct, why couldn't I just call these constructors in the body of Generator's constructor? – Mercutio Aug 29 '17 at 00:37
  • @user1318209 Doing that means you're default constructing the members and then *assigning* them a different value in the constructor body. That wouldn't compile in this case because `variate_generator` is not default constructible. See https://stackoverflow.com/q/926752/241631 for a discussion of why to prefer the mem-initializer list – Praetorian Sep 04 '17 at 13:51