C++ подавляет автоматическую инициализацию и разрушение

Как каждый подавляет автоматическую инициализацию и разрушение типа? В то время как это замечательно это 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
задан Travis Gockel 18 April 2010 в 15:12
поделиться

5 ответов

Вы можете создать массив как массив из char s, а затем использовать размещение 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];
    }
3
ответ дан 14 December 2019 в 01:04
поделиться

Вы можете изучить 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 };
4
ответ дан 14 December 2019 в 01:04
поделиться

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

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 и любое отслеживание того, что создано, а что нет (вызов деструктора в месте, для которого вы не вызывали конструктор, вероятно, является неопределенным поведением ). Кроме того, выделение и освобождение вектора STL абстрагируется с помощью распределителей (второй аргумент шаблона вектора ).

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

1
ответ дан 14 December 2019 в 01:04
поделиться

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

0
ответ дан 14 December 2019 в 01:04
поделиться

Этот код:

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

дает именно тот результат, который вам нужен - я не уверен, в чем ваш вопрос.

1
ответ дан 14 December 2019 в 01:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: