C++ Suppress Automatic Initialization and Destruction

前端 未结 5 1798
予麋鹿
予麋鹿 2021-01-03 02:15

How does one suppress the automatic initialization and destruction of a type? While it is wonderful that T buffer[100] automatically initializes all the elemen

相关标签:
5条回答
  • 2021-01-03 02:37

    You can create the array as array of chars and then use placement new to create the elements when needed.

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

    After re-reading your question it seems that you want a sparse array, this sometimes goes by the name of map ;o) (of course the performance characteristics are different...)

    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];
        }
    
    0 讨论(0)
  • 2021-01-03 02:38

    You may want to look into 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];
    };
    

    A specialization is done for class type that wraps them into an optional, thus calling the constructor/destructor lazily. For non-class types, we don't need that wrapping. Not wrapping them means we can treat &a[0] as a contiguous memory area and pass that address to C functions that want an array. boost::in_place will create the class types in-place, without using a temporary T or its copy constructor.

    Not using inheritance or private members allow the class to stay an aggregate, allowing a convenient form of initialization

    // only two strings are constructed
    Array<std::string, 10> array = { a, b };
    
    0 讨论(0)
  • 2021-01-03 02:39

    You can have a look at the way it's done with the STL containers, but I doubt you can spare yourself mallocs and frees

    0 讨论(0)
  • 2021-01-03 02:48

    If you want to be like vector, you should do something like this:

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

    This code is obviously for demonstration only, I have omitted my_vector's copy-constructor and any tracking on what's created and what not (calling destructor on a location you haven't called constructor for is probably undefined behavior). Also, STL's vector allocations and deallocations are abstracted away through the use of allocators (the second template argument of vector).

    Hope that helps

    0 讨论(0)
  • 2021-01-03 02:51

    This code:

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

    has exactly the output you want - I'm not sure what your question is.

    0 讨论(0)
提交回复
热议问题