-4

I am trying (struggling) writing a generic vector class using std::unique_ptr. In my constructor I get this exception thrown:

Exception thrown: write access violation.
std::unique_ptr<int [0],std::default_delete<int [0]> >::operator[](...) returned nullptr.

This is the associated function:

template <class T>
Vector<T>::Vector(int n, const T &value) {
    capacity = (n > initial_capacity) ? n : initial_capacity;
    size = n;
    for (int i = 0; i < n; i++) {
        data[i] = value;
    }
}

I also get an error here in the main.cpp file:

assert(nullVector.getCapacity() == 100); 

I believe this is because I did not set the capacity in the std::unique_ptr if that is even possible.

Here is part of my header file:

#ifndef Vector_h
#define Vector_h



template <class T>
class Vector {
private:

    static constexpr int initial_capacity = 100;

    // Instance variables
    int capacity = 0;
    int size = 0;
    std::unique_ptr<T[]> data = nullptr;

    void deepCopy(const Vector<T> &source) {
        capacity = source.size + initial_capacity;
        for (int i = 0; i < source.size; i++) {
            data[i] = source.data[i];
        }
        size = source.size;
    }

    void expandCapacity() {
        auto oldData = std::move(data);
        capacity *= 2;
        for (int i = 0; i < size; i++) {
            data[i] = oldData[i];
        }
    }

public:

    // Constructors
    Vector() = default;                                 // empty constructor
    Vector(int n, const T &value);                      // constructor
    Vector(Vector<T> const &vec);                       // copy constructor
    Vector<T>& operator=(Vector<T> const &rhs);         // assignment operator

    // Rule of 5
    Vector(Vector<T> &&move) noexcept;                  // move constructor
    Vector& operator=(Vector<T> &&move) noexcept;       // move assignment operator
    ~Vector();                                          // destructor

    // Overload operators
    T& operator[](int index);
    T const& operator[](int index) const;
    bool operator==(const Vector<T>&) const;

    //Vector<T>& operator+=(const Vector<T> &other) {
    //  Vector<T> newValue(size + other.size);

    //  std::copy(this->data, this->data + this->size, newValue.data);
    //  std::copy(other.data, other.data + other.size, newValue.data + this->size);

    //  newValue.swap(*this);
    //}

    friend Vector<T>& operator+(Vector<T> &source1, Vector<T> &source2) {
        int n = source1.getSize() + source2.getSize();
        Vector<T> newSource(n,0);
        for (int i = 0; i < source1.size; i++) {
            newSource[i] = source1[i];
        }

        for (int i = 0; i < source2.size; i++) {
            newSource[i + source1.getSize()] = source2[i];
        }

        return newSource;
    }

    friend std::ostream& operator<<(std::ostream &str, Vector<T> &data) {
        data.display(str);
        return str;
    }

    // Member functions
    void swap(Vector<T> &other) noexcept;
    void display(std::ostream &str) const;
    int getSize() const { return size; }
    int getCapacity() const { return capacity; }
    bool empty() const { return size == 0; }
    void clear() { size = 0; }
    T get(int index) const;
    void set(int index, const T &value);
    void set(int index, T &&value);
    void insert(int index, const T &value); 
    void insert(int index, T &&value);
    void remove(int index);
    void push_back(const T &value);
    void pop_back();

};



template <class T>
Vector<T>::Vector(int n, const T &value) {
    capacity = (n > initial_capacity) ? n : initial_capacity;
    size = n;
    for (int i = 0; i < n; i++) {
        data[i] = value;
    }
}

Here is part of the main.cpp file:

#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <cassert>
#include <ostream>
#include "Vector.h"


int main() {

    ///////////////////////////////////////////////////////////////////////
    ///////////////////////////// VECTOR //////////////////////////////////
    ///////////////////////////////////////////////////////////////////////
    Vector<int> nullVector;                        // Declare an empty Vector
    assert(nullVector.getSize() == 0);                 // Make sure its size is 0
    assert(nullVector.empty());                    // Make sure the vector is empty
    assert(nullVector.getCapacity() == 100);          // Make sure its capacity is greater than 0


}
  • 1
    You aren't calling that constructor, you're calling the empty constructor (at least in the code you posted). – john Aug 05 '18 at 21:59
  • Please don't post commented-out code - it makes it hard to know what you are actually asking about. –  Aug 05 '18 at 22:00
  • please create a minimalist version of your code to make it easier to debug. Take out all unnecessary parts. – GenericDeveloperProfile Aug 05 '18 at 22:06

1 Answers1

3

There is no such thing as a "capacity" of a unique_ptr. All an std::unique_ptr does is it holds on to a dynamically allocated object. It does not allocate an object by itself. Use std::make_unique() or new to create an new object and assign to your unique_ptr to hold on to.

I don't see you allocating any memory anywhere in your code. Unless you do allocate memory for your vector somewhere in a piece of code you didn't show, your data will just point to nullptr and trying to dereference it will crash (or worse). At least your expandCapacity() method does not seem to allocate any memory…

You probably should have a look at some material to learn about unique_ptr and smart pointers in general. For example: How to declare std::unique_ptr and what is the use of it? or this.

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • void expandCapacity() { auto oldData = std::move(data); capacity *= 2; data = std::make_unique(capacity); for (int i = 0; i < size; i++) { data[i] = oldData[i]; } } –  Aug 06 '18 at 00:08
  • I tried what you suggested but it still does not work –  Aug 06 '18 at 00:08