SU3
SU3

Reputation: 5387

allocating memory for class member

I'm trying to provide a uniform interface for two similar types, one dealing with doubles and the other with floats.

class float_type {
  float_type() { /* does floaty stuff */ }
  float f();
};
class double_type {
  double_type() { /* does doubly stuff */ }
  double f();
};

I want to write a class that allocates one or the other depending on what the program needs to do. I'm perfectly fine with the result of float_type::f() being converted to double. In fact, it happens anyway. I tried to write it like this:

class union_type {
  bool is_double;
  char mem[ sizeof(double_type) > sizeof(float_type)
          ? sizeof(double_type) : sizeof(float_type) ];
public:
  float_or_double_value_reader(bool is_double)
  : is_double(is_double)
  {
    if (is_double) new(mem) double_type();
    else new(mem) float_type();
  }

  ~float_or_double_value_reader() {
    if (is_double) delete static_cast<double_type*>(mem);
    else           delete static_cast< float_type*>(mem);
  }

  double f() {
    return (is_doubled
      ? static_cast<double_type*>(mem)->f()
      : static_cast< float_type*>(mem)->f()
    );
  }
};

But I get invalid static_cast from type 'char [128]' to type 'double_type'.

I know I could add a member pointers to point to what new returns, but that would be redundant, since I already know where mem is located, so I want to avoid that.

If I use reinterpret_cast instead, I get free(): invalid pointer: at runtime when the union_type is destroyed.

What's the appropriate method of casting here?

Upvotes: 2

Views: 49

Answers (3)

reinterpret_cast should be the appropriate method of casting.

However, you can't simply delete reinterpret_cast<double_type*>(mem) because that will not only destroy the object, but also free the memory as if it was allocated with new - which it wasn't.

You can use reinterpret_cast<double_type*>(mem)->~double_type(); to destroy the object without attempting to free the memory.

Of course the above applies to float_type as well.

Upvotes: 2

txtechhelp
txtechhelp

Reputation: 6752

You could use a template base class:

#include <iostream>

template < typename T >
class base_decimal
{
    public:
        base_decimal(T data) : _data(data) {}
        virtual ~base_decimal() {}
        T f() { return this->_data; }
        base_decimal& operator=(T val) { this->_data = val; }
        operator T() { return this->_data; }
        friend std::ostream& operator<<(std::ostream& os, const base_decimal& bd)
        {
            os << bd._data;
            return os;
        }
    private:
        T _data;
};

class float_type : public base_decimal<float>
{
    public:
        float_type(float f) : base_decimal<float>(f)
        {
            // do float stuff
        }
};

class double_type : public base_decimal<double>
{
    public:
        double_type(double d) : base_decimal<double>(d)
        {
            // do double stuff
        }
};

int main(int argc, char* argv[])
{
    float_type f = 1.2f;
    double_type d = 2.2;

    std::cout << "f = " << f << std::endl;
    std::cout << "d = " << d << std::endl;

    double rd = d;
    double rf = f;

    std::cout << "rf = " << rf << std::endl;
    std::cout << "rd = " << rd << std::endl;

    return 0;
}

Upvotes: 0

Sarvex
Sarvex

Reputation: 785

A better option would be to provide casting operator.

I would have provided a implicit double casting operator to the float class to achieve the same interface

Upvotes: 0

Related Questions