2017-01-19 2 views
2

Я пытаюсь обеспечить единый интерфейс для двух аналогичных типов, один из которых относится к double s, а другой - к float.выделение памяти для члена класса

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

Я хочу написать класс, который выделяет тот или иной в зависимости от того, что программа должна делать. Я отлично справляюсь с результатом преобразования float_type::f() в двойное. На самом деле это так или иначе. Я попытался написать это:

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() 
    ); 
    } 
}; 

Но я получаю invalid static_cast from type 'char [128]' to type 'double_type'.

Я знаю, что я мог бы добавить указатели членов, чтобы указать на то, что new возвращается, , но это было бы излишним, так как я уже знаю, где mem находится, , поэтому я хочу, чтобы избежать этого.

Вместо этого я использую reinterpret_cast, я получаю free(): invalid pointer: во время выполнения, когда union_type уничтожен.

Какой способ литья здесь?

+1

ли 'data' должен быть' mem'? Во всяком случае, когда вы используете новое место размещения, вы не используете 'delete' для объекта, потому что он попытается освободить память. Вместо этого явно вызовите соответствующий деструктор (например, 'reinterpret_cast (data) -> ~ double_type();') –

+0

Да, 'data' является' mem'. Извинения за несогласованность. Позвольте мне исправить это. – SU3

+0

@ChristopherOicles Спасибо. Я не знал об этом. Не могли бы вы написать это как ответ? – SU3

ответ

2

reinterpret_cast должен быть приемлемым методом литья.

Однако вы не можете просто delete reinterpret_cast<double_type*>(mem), потому что это не только уничтожит объект, но и освободит память, как если бы она была выделена new - которой она не была.

Вы можете использовать reinterpret_cast<double_type*>(mem)->~double_type(); для уничтожения объекта, не пытаясь освободить память.

Конечно, вышесказанное относится к float_type.

0

Лучшим вариантом было бы предоставить оператора литья.

я бы обеспечил оператор неявного double литейной к классу поплавка, чтобы достигнуть того же самого интерфейса

+0

Это будет работать в некоторых случаях, но в моем случае конструкторы 'float_type' и' double_type' выполняют разные вещи, и мне нужно позвонить один или другой, но не оба. Динамическое распределение - это, конечно, вариант, но почему бы не сделать это статически? – SU3

+0

В этом случае лучшим вариантом было бы предпочесть композицию над наследованием для более чистого интерфейса. – Sarvex

0

Вы можете использовать шаблон базового класса:

#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; 
} 
+0

Это определенно применимо в другой ситуации, но мне нужно было построить один или другой (не оба) для данного аргумента конструктора. И два типа из библиотеки в моем случае, и я не контролирую их. – SU3

Смежные вопросы