C ++ подавляет автоматическую инициализацию и уничтожение - PullRequest
5 голосов
/ 18 апреля 2010

Как подавить автоматическую инициализацию и уничтожение типа?Хотя замечательно, что T buffer[100] автоматически инициализирует все элементы buffer и уничтожает их, когда они выпадают из области видимости, такое поведение я не хочу.

#include <iostream>

static int created   = 0,
           destroyed = 0;

struct S
{
    S()
    {
        ++created;
    }
    ~S()
    {
        ++destroyed;
    }
};

template <typename T, size_t KCount>
class fixed_vector
{
private:
    T m_buffer[KCount];
public:
    fixed_vector()
    {
        // some way to suppress the automatic initialization of m_buffer
    }

    ~fixed_vector()
    {
        // some way to suppress the automatic destruction of m_buffer
    }
};

int main()
{
    {
        fixed_vector<S, 100> arr;
    }

    std::cout << "Created:\t"   << created   << std::endl;
    std::cout << "Destroyed:\t" << destroyed << std::endl;
    return 0;
}

Вывод этой программыэто:

Created:    100
Destroyed:  100

Мне бы хотелось, чтобы это было:

Created:    0
Destroyed:  0

Моя единственная идея - сделать m_buffer неким тривиально сконструированным и разрушенным типом, таким как char, а затем положиться наoperator[] обернуть мне указатель по математике, хотя это кажется ужасно взломанным решением.Другим решением было бы использовать malloc и free, но это дает уровень косвенности, которого я не хочу.


Причина, по которой я этого хочу, заключается в том, что я делаю контейнер иЯ не хочу платить за инициализацию тех вещей, которые я не буду использовать.Например, если моя функция main была:

int main()
{
    {
        std::vector<S> vec;
        vec.reserve(50);
    }

    std::cout << "Created:\t"   << created   << std::endl;
    std::cout << "Destroyed:\t" << destroyed << std::endl;
    return 0;
}

Вывод будет правильным:

Created:    0
Destroyed:  0

Ответы [ 5 ]

4 голосов
/ 18 апреля 2010

Вы можете посмотреть на boost::optional

template <typename> struct tovoid { typedef void type; };

template <typename T, size_t KCount, typename = void>
struct ArrayStorage {
  typedef T type;
  static T &get(T &t) { return t; }
};

template <typename T, size_t KCount>
struct ArrayStorage<T, KCount, typename tovoid<int T::*>::type> {
  typedef boost::optional<T> type;
  static T &get(boost::optional<T> &t) {
    if(!t) t = boost::in_place();
    return *t;
  }
};

template <typename T, size_t KCount>
class Array
{
public:
    T &operator[](std::ptrdiff_t i) {
      return ArrayStorage<T, KCount>::get(m_buffer_[i]);
    }

    T const &operator[](std::ptrdiff_t i) const {
      return ArrayStorage<T, KCount>::get(m_buffer_[i]);
    }

    mutable typename ArrayStorage<T, KCount>::type m_buffer_[KCount];
};

Специализация делается для типа класса, который оборачивает их в optional, таким образом вызывая конструктор / деструктор лениво. Для типов, не относящихся к классам, нам не нужна эта упаковка. Не оборачивая их, мы можем рассматривать &a[0] как непрерывную область памяти и передавать этот адрес функциям C, которые хотят получить массив. boost::in_place создаст типы классов на месте, без использования временного T или его конструктора копирования.

Не используя наследование или закрытые члены позволяют классу оставаться агрегированным, что позволяет использовать удобную форму инициализации

// only two strings are constructed
Array<std::string, 10> array = { a, b };
3 голосов
/ 18 апреля 2010

Вы можете создать массив как массив char с, а затем использовать размещение new для создания элементов при необходимости.

template <typename T, size_t KCount>
class Array
{
private:
    char m_buffer[KCount*sizeof(T)]; // TODO make sure it's aligned correctly

    T operator[](int i) {
        return reinterpret_cast<T&>(m_buffer[i*sizeof(T)]);
    }

После повторногочитая ваш вопрос, кажется, что вам нужен разреженный массив, иногда он называется map ; o) (конечно, характеристики производительности разные ...)

template <typename T, size_t KCount>
class SparseArray {
    std::map<size_t, T> m_map;
public:
    T& operator[](size_t i) {
        if (i > KCount)
            throw "out of bounds";
        return m_map[i];
    }
1 голос
/ 18 апреля 2010

Если вы хотите быть похожим на вектор, вы должны сделать что-то вроде этого:

template <typename T>
class my_vector
{
    T* ptr; // this is your "array"
    // ...
    public:

    void reserve(size_t n)
    {
        // allocate memory without initializing, you could as well use malloc() here
        ptr = ::operator new (n*sizeof(T)); 
    }

    ~my_vector()
    {
        ::operator delete(ptr); // and this is how you free your memory
    }

    void set_element(size_t at, const T& element = T())
    {
        // initializes single element
        new (&ptr[at]) T(element); // placement new, copies the passed element at the specified position in the array
    }

    void destroy_element(size_t at)
    {
        ptr[at].~T(); // explicitly call destructor
    }
};

Этот код, очевидно, предназначен только для демонстрации, я опустил конструктор копирования my_vector и отслеживание того, что создано, а что нет (вызов деструктора для местоположения, для которого вы не вызывали конструктор, вероятно, является неопределенным поведением). Кроме того, vector выделения и освобождения STL абстрагируются посредством использования распределителей (второй аргумент шаблона vector).

Надеюсь, что поможет

1 голос
/ 18 апреля 2010

Этот код:

#include <iostream>
#include <vector>
using namespace std;

int created = 0, destroyed = 0;

struct S
{
    S()
    {
        ++created;
    }
    S(const S & s ) {
        ++created;
    }
    ~S()
    {
        ++destroyed;
    }
};

int main()
{
    {
        std::vector<S> vec;
        vec.reserve(50);
    }

    std::cout << "Created:\t"   << created   << std::endl;
    std::cout << "Destroyed:\t" << destroyed << std::endl;
    return 0;
}

имеет именно то, что вы хотите - я не уверен, что ваш вопрос.

0 голосов
/ 18 апреля 2010

Вы можете посмотреть, как это делается с контейнерами STL, но я сомневаюсь, что вы можете сэкономить malloc s и free s

...