Class template not working with std::string or class object

0

I am new to C++ templates and I've been struggling to understand why the append() function that I've implemented for my DynamicVector template class:

template<class EntityType>
void DynamicVector<EntityType>::append(const EntityType newEntity)
{
    if (this->size == this->capacity) {
        resize();
    }

    this->size += 1;
    this->data[this->size - 1] = newEntity;
}

does not work for every data type.

Creating and appending a new element to a dynamic vector of int or char works just fine, but when it comes to a std::string or a class object, even though the declaration appears to work all right(i.e. DynamicVector<std::string> test_dynamicVectorString or DynamicVector<MyClassObject> test_dynamicVectorMyClassObject), when I try to append an element to the vector:

DynamicVector<std::string> test_dynamicVector;
test_dynamicVector.append("asdasd");

access violation is thrown:

Exception thrown at 0x6ED640C9 (vcruntime140d.dll) in DynVect.exe : 0xC0000005: Access violation writing location 0x0D3539A8.

I have a dynamically allocated array inside the DynamicVector class(EntityType* data = new EntityType[capacity];), where the appended elements are supposed to get stored, and actually are correctly stored when the EntityType is int or char.

But when I try to store strings, debug and step into the append() function, at my Autos it appears that the value of data is:

0x0146539 Error reading characters of string>.<

In fact, the same value appears even when I step into the constructor and data gets initialized within the member initialization list:

template<class EntityType>
DynamicVector<EntityType>::DynamicVector() : size{ 0 }, capacity{ 1 }, data{ new EntityType[capacity] }
{
}

This is the code for DynamicVector class.

DynamicVector.h

#pragma once

#include "ErrorMessages.h"


template <class EntityType>
class DynamicVector
{

public:
    DynamicVector();
    DynamicVector(const DynamicVector& original_dynamicVector);
    ~DynamicVector();

    void append(const EntityType newEntity);
    void erase(unsigned int indexToRemove);
    void clear();
    size_t get_size();
    EntityType& operator[](unsigned int index);
    DynamicVector& operator=(const DynamicVector& source);

private:
    const int GROWTH_RATE{ 2 };

    EntityType* data = new EntityType[capacity];
    size_t capacity;
    size_t size;

    void resize();
    bool validIndex(int index);
};

/// Contstructor
template<class EntityType>
DynamicVector<EntityType>::DynamicVector() : size{ 0 }, capacity{ 1 }, data{ new EntityType[capacity] }
{
}

/// Copy constructor
template<class EntityType>
DynamicVector<EntityType>::DynamicVector(const DynamicVector& original_dynamicVector)
{
    this->capacity = original_dynamicVector.capacity;
    this->size = original_dynamicVector.size;

    this->data = new EntityType[this->capacity];
    for (unsigned int i = 0; i < this->size; ++i) {
        this->data[i] = original_dynamicVector.data[i];
    }
}

/// Destructor
template<class EntityType>
DynamicVector<EntityType>::~DynamicVector()
{
}


/// <summary>
/// Appends the specified new entity to the dynamic vector
/// </summary>
/// <param name="newEntity">The new entity.</param>
template<class EntityType>
void DynamicVector<EntityType>::append(const EntityType newEntity)
{
    if (this->size == this->capacity) {
        resize();
    }

    this->size += 1;
    this->data[this->size - 1] = newEntity;
}


/// <summary>
/// Resizes the dynamic vector, when the capacity gets filled.
/// </summary>
template<class EntityType>
void DynamicVector<EntityType>::resize()
{
    EntityType* resizedSpace{ new EntityType[this->capacity *= GROWTH_RATE] };

    for (int i = 0; i < this->size; ++i) {
        resizedSpace[i] = this->data[i];
    }

    // delete[] this->data;
    this->data = resizedSpace;
}
c++
class
templates

2 Answers

1

You have two problems that both lead to undefined behavior (and for the same reason, uninitialized variables).

The first and most obvious problem is these two lines in the class definition:

EntityType* data = new EntityType[capacity];
size_t capacity;

Here you will initialize data with the help of the uninitialized member variable capacity. Using capacity when it's uninitialized will lead to undefined behavior.

Since you initialize both capacity and data in the constructor you don't need the inline initialization of data.


The second problem is the initialization order in the constructor.

Member variables in the constructor initializer list will be initialized in the order of declaration in the class, not the order in the constructor initializer list.

Because you declare the member variables in the order

EntityType* data;
size_t capacity;

the member capacity will be uninitialized when you use it for the initialization of data. This also leads to undefined behavior.

You need to change the declaration order to

size_t capacity;
EntityType* data;

From this initialization order reference:

... non-static data members are initialized in order of declaration in the class definition.

[Emphasis mine]

answered on Stack Overflow Mar 20, 2020 by Some programmer dude • edited Mar 20, 2020 by Some programmer dude
0

EntityType* data = new EntityType[capacity]; in the class declaration? The only reason that's not a memory leak is because it's Undefined Behavior; capacity is not initialized.

answered on Stack Overflow Mar 20, 2020 by MSalters

User contributions licensed under CC BY-SA 3.0