Meena Alfons
Meena Alfons

Reputation: 1230

Initialize new[] using copy constructor

I believe this is a simple question with probably a not simple answer.

Here is the code:

template<typename T>
T* copy(T* original, int size) {
    T* result = new T[size];
    // At this point the default constructor of all new T objects have been called.

    for(int i = 0; i < size; ++i) {
        // This will call the assignment operator= on all new T objects
        result[i] = original[i];
    }
    return result;
}

Question:

Is there a way to initialize the newly allocated memory using the copy constructor of T instead of using default constructor followed by assignment operator?

The purpose is to copy each element to its analogous element in the new array using the copy constructor of T.

I imagine there could be a way to do that by allocating memory using malloc, then calling the copy constructor for each element but I don't know how.

Here is an example solution from my imagination. If this is correct or this is the best we can get, tell me. Or propose a better solution:

template<typename T>
T* copy(T* original, int size) {
    T* result = malloc(sizeof(T)*size);
    // At this point the default constructor of all new T objects have been called.

    for(int i = 0; i < size; ++i) {
        T t(original[i]);
        memcpy(result+i*sizeof(T), &t, sizeof(T));
    }
    return result;
}

Note: Raw pointers are being used for simplicity.

Note 2: I don't need a vector. This pattern will be used to copy the underlying data structure of more complicated objects.

Upvotes: 4

Views: 178

Answers (2)

user1143634
user1143634

Reputation:

You will have to allocate memory by any other means, but keep in mind that size * sizeof(T) can overflow. std::allocator takes care of this.

Use std::uninitialized_copy/std::uninitialized_copy_n to perform the copy:

template<typename T>
T* copy(T* original, int size) {
    std::allocator<T> alloc;
    T* result = alloc.allocate(size);
    try {
        std::uninitialized_copy_n(original, size, result);
    } catch (...) {
        alloc.deallocate(result, size);
        throw;
    }
    return result;
}

Later you can use std::destroy/std::destroy_n to destroy them and deallocate memory:

template<typename T>
void destroy(T* ptr, int size)
{
    std::destroy_n(ptr, size);
    std::allocator<T>().deallocate(ptr, size);
}

This should work unless you need to be able to delete them with operator delete[] - in which case there is no solution for this.

If you are implementing a custom container, you can use template allocator like standard containers do:

template<typename T, typename Allocator = std::allocator<T>>
struct container
{
    [[no_unique_address]] Allocator allocator;
    ...
};

Upvotes: 4

bolov
bolov

Reputation: 75707

For the new operator I don't think so.

But yes there is. It's called std::vector:

template<typename T>
std::vector<T> copy(T* original, int size) {
   return std::vector<T>{original, original + size};
}

Because you don't follow RAII and use owning raw pointers your code is not memory leak free, so don't do that! Use C++ properly.

Upvotes: 0

Related Questions