I'm trying to set up the overloaded operator '=' for a custom Array class for practice, but it seems to be causing a runtime error.
class Array {
private:
static int numberOfElements; //class static variable
int size;
int* numbers;
public:
Array(int);
Array(const Array&);
~Array();
int getSize();
static int getNumberOfElements();
Array& operator =(const Array&);
};
This overloaded operator function produces the correct output, but with a runtime error:
Array& Array::operator =(const Array& newArray) {
numberOfElements = numberOfElements - size + newArray.size;
size = newArray.size;
for (int i = 0; i < size; i++)
numbers[i] = newArray.numbers[i];
return *this;
}
Before, I had
Array& Array::operator =(const Array& newArray) {
delete[] numbers;
numberOfElements = numberOfElements - size + newArray.size;
size = newArray.size;
numbers = new int[size];
for (int i = 0; i < size; i++)
numbers[i] = newArray.numbers[i];
return *this;
}
Which does not produce a runtime, but creates an array filled with garbage. numberOfElements is just tracking the total elements in all Arrays, and shouldn't be a factor in the error. I'm certain the issue is the dynamic allocation, but I can't seem to logically figure out why it would throw a runtime if I'm only overwriting the original array with newArray, and why the latter is filling with garbage even though the allocated array is being set to newArray's elements.
If newArray.size is larger than this->size, your operator= trashes memory. It needs to reallocate its numbers[] array to account for the larger size, which you were originally doing (just not in an exception-safe manner).
If newArray.size is not larger than this->size, no reallocation is needed, just copy the values from newArray.numbers into this->numbers as-is, making sure to not exceed the smaller of newArray.size or this->size.
Try something more like this:
Array& Array::operator=(const Array& newArray)
{
if (&newArray != this)
{
if (size != newArray.size)
{
numberOfElements -= size;
delete[] numbers;
numbers = NULL;
size = 0;
numbers = new int[newArray.size];
size = newArray.size;
numberOfElements += size;
}
for (int i = 0; i < size; ++i)
numbers[i] = newArray.numbers[i];
}
return *this;
}
Which can then be made safer using the copy-swap idiom using your existing copy constructor:
Array::Array(int num)
{
size = num;
numbers = new int[num];
numberOfElements += num;
}
Array::Array(const Array &srcArray)
{
size = src.size;
numbers = new int[size];
for (int i = 0; i < size; ++i)
numbers[i] = srcArray.numbers[i];
numberOfElements += size;
}
Array::~Array()
{
delete[] numbers;
numberOfElements -= size;
}
#include <algorithm>
Array& Array::operator=(const Array& newArray)
{
if (this != &newArray)
{
if (size != newArray.size)
{
Array tmp(newArray);
std::swap(numbers, tmp.numbers);
std::swap(size, tmp.size);
}
else
{
for (int i = 0; i < size; ++i)
numbers[i] = newArray.numbers[i];
}
}
return *this;
}
Related
I have been making a custom vector class, however I have been bumping into a problem. The problem being that my vector just won't resize, the size stays 0.
Anyone know what the problem is?
Thanks in advance!
void resize(const int newSize) {
// If size is zero, give error
if (newSize < 0) {
throw std::out_of_range("Vector");
}
// If the given size is smaller than the size that it is now, just ignore
else if (newSize < capacity) {
return;
}
// Else, make a new array and copy everything over
T* newArr = new T[newSize];
for (int i = 0; i < capacity; i++) {
newArr[i] = data[i];
}
delete[] data;
capacity = newSize;
data = newArr;
}
EDIT 1:
Here is the push_back function where resize is needed:
template <typename T> void pushBack(Vector<T>& vector, T& value){
unsigned int oldSize = vector.size();
vector.resize(oldSize+1);
vector.at(oldSize) = value;
}
EDIT 2:
Here are the vector deffinitions:
Vector(const int newSize) {
if (newSize < 0) {
throw std::out_of_range("Vector");
}
data = new T[newSize];
capacity = newSize;
actualSize = 0;
}
Im trying to create this and add push back functionality. How can i do it? I came up with this code but im very confused for 2 days because it gives this output - | -842150451 | -842150451 | -842150451 | -842150451 | -842150451 | -842150451 | -842150451 | -842150451
I will be thankful if someone can tell me how this function push_back should look or at least point me in the right direction. Also i would be thankful if you tell me where this code is doing wrong?
struct IntArray{
private:
int k;
int* first_cell;
int size; // currently occupied elements
int capacity = 8; // size of the allocated memory
public:
void create() {
first_cell = (int*)malloc(capacity * sizeof(int));
}
void random_numbers_fill(int limit) {
srand(time(NULL));
for (k = 0; k < capacity; k++) {
first_cell[k] = rand() % limit;
}
}
void push_back(int number) {
if (size == capacity) {
int* new_arr;
capacity *= 2;
new_arr = (int*)malloc(capacity * sizeof(int));
for (k = 0; k < capacity; k++) {
new_arr[k] = first_cell[k];
}
free(first_cell);
first_cell = new_arr;
first_cell[size] = number;
size++;
}
}
void print() {
for (k = 0; k < capacity; k++) {
printf("| %d ", first_cell[k]);
}
First, create() should be changed into an actual constructor. And you are missing a destructor to free the array, as well as copy/move constructors and copy/move assignment operators to manage the array (per the Rule of 3/5/0).
Second, you should be using new[] instead of malloc().
Third, regarding your push_back(), it is not a bad attempt, but it is buggy. You are not adding the number to the array at all if the current size is less than the current capacity (and your constructor is not initializing size at all). And when you do resize + copy the array, you are copying too many ints from the old array. The old array is size elements, but you are trying to copy the newly increased capacity elements from it.
With that said, try something more like this instead:
#include <iostream>
#include <algorithm>
#include <utility>
class IntArray{
private:
int* first_cell = nullptr;
int size = 0; // currently occupied elements
int capacity = 8; // size of the allocated memory
public:
IntArray()
{
first_cell = new int[capacity];
}
IntArray(const IntArray &src)
: size(src.size), capacity(src.capacity)
{
first_cell = new int[capacity];
std::copy_n(src.first_cell, size, first_cell);
}
IntArray(IntArray &&src)
: first_cell(src.first_cell), size(src.size), capacity(src.capacity)
{
src.first_cell = nullptr;
src.size = src.capacity = 0;
}
~IntArray()
{
delete[] first_cell;
}
IntArray& operator=(IntArray rhs)
{
IntArray temp(std::move(rhs));
std::swap(first_cell, temp.first_cell);
std::swap(size, temp.size);
std::swap(capacity, temp.capacity);
return *this;
}
void push_back(int number)
{
if (size == capacity)
{
int new_cap = capacity * 2;
int* new_arr = new int[new_cap];
for (int k = 0; k < size; ++k) {
new_arr[k] = first_cell[k];
}
delete[] first_cell;
first_cell = new_arr;
capacity = new_cap;
}
first_cell[size] = number;
++size;
}
void print() const
{
for (int k = 0; k < size; ++k) {
std::cout << "| " << first_cell[k] << " ";
}
}
};
That being said, you should just get rid of your manual array and use std::vector<int> instead, as it handles all of these details for you, eg:
#include <iostream>
#include <vector>
class IntArray{
private:
std::vector<int> vec;
public:
IntArray()
{
vec.reserve(8);
}
void push_back(int number)
{
vec.push_back(number);
}
void print() const
{
for (int number : vec) {
std::cout << "| " << number << " ";
}
}
};
You never initialize size in create.
Your push_back has two problems. You don't copy the number that is pushed unless you grow the array (the last two statements should be outside the if block), and the condition in the for loop should compare with size, not capacity.
print also has the wrong condition in the for loop.
Hello i have this IntArray i want to convert to accept generic types:
class IntArray {
private:
int* first_cell = nullptr;
int size = 0; // currently occupied elements
int capacity = 8; // size of the allocated memory
public:
IntArray() {
first_cell = new int[capacity]; // Declare the array in memory
}
IntArray(const IntArray& src)
: size(src.size),
capacity(src.capacity)
{
first_cell = new int[capacity];
std::copy_n(src.first_cell, size, first_cell);
}
IntArray(IntArray&& src)
: first_cell(src.first_cell),
size(src.size),
capacity(src.capacity)
{
src.first_cell = nullptr;
src.size = src.capacity = 0;
}
~IntArray() {
delete[] first_cell;
}
IntArray& operator=(IntArray rhs) {
IntArray temp(std::move(rhs));
std::swap(first_cell, temp.first_cell);
std::swap(size, temp.size);
std::swap(capacity, temp.capacity);
return *this;
}
void push_back(int number) {
if (size == capacity) {
int new_cap = capacity * 2; // increased capacity
int* new_arr = new int[new_cap]; // new arr with new capacity
for (int k = 0; k < size; ++k) {
new_arr[k] = first_cell[k]; // copy data from frist array
}
delete[] first_cell; // remove first array
first_cell = new_arr;
capacity = new_cap;
}
first_cell[size] = number;
++size;
}
int length() {
return size;
}
int index_of(int number) {
for (int k = 0; k < size; k++) {
if (number == first_cell[k]) {
return k;
}
}
return -1;
}
void print(char symb) {
for (int k = 0; k < size; ++k) {
std::cout << first_cell[k] << symb;
}
}
};
How can i convert this array it to accept any type like generic array? I want to be able to accept any types is this possible? And how do i check the types? If you can point me in the right direction because i have no idea how it can be done? Thanks.
I figured it out;
template<class T>
class List {
private:
T* first_cell = nullptr;
int size = 0; // currently occupied elements
int capacity = 8; // size of the allocated memory
public:
List() {
first_cell = new T[capacity]; // Declare the array in memory
}
List(const List& src)
: size(src.size),
capacity(src.capacity)
{
first_cell = new T[capacity];
std::copy_n(src.first_cell, size, first_cell);
}
List(List&& src)
: first_cell(src.first_cell),
size(src.size),
capacity(src.capacity)
{
src.first_cell = nullptr;
src.size = src.capacity = 0;
}
~List() {
delete[] first_cell;
}
List& operator=(List rhs) {
List temp(std::move(rhs));
std::swap(first_cell, temp.first_cell);
std::swap(size, temp.size);
std::swap(capacity, temp.capacity);
return *this;
}
void push_back(int number) {
if (size == capacity) {
int new_cap = capacity * 2; // increased capacity
T* new_arr = new T[new_cap]; // new arr with new capacity
for (int k = 0; k < size; ++k) {
new_arr[k] = first_cell[k]; // copy data from frist array
}
delete[] first_cell; // remove first array
first_cell = new_arr;
capacity = new_cap;
}
first_cell[size] = number;
++size;
}
int length() {
return size;
}
int first_index_of(int number) {
for (int k = 0; k < size; k++) {
if (number == first_cell[k]) {
return k;
}
}
return -1;
}
void print(char symb) {
for (int k = 0; k < size; ++k) {
std::cout << first_cell[k] << symb;
}
}
};
I simulated a vector but the constructor doesn't work; when I call pop() function it assigns garbage value to my old object in vector class.
vector(vector &v) {
vec = new T[v.size()];
memcpy(vec, v,v.size());
size_arr = v.size();
}
here's entire code:
#include <iostream>
using namespace std;
template <typename T>
class vector {
int size_arr;
T * vec = new T;
public:
vector(/*int _size*/) {
vec = new T[0];
size_arr = 0;
};
~vector() {
size_arr = 0;
delete[] vec;
};
vector(vector &v) {
vec = new T[v.size()];
memcpy(vec, v,v.size());
size_arr = v.size();
}
void push_back(T data) {
T *temp = new T[size_arr + 1];
for (int i = 0; i < size_arr; i++)
temp[i] = vec[i];
temp[size_arr] = data;
size_arr++;
delete[] vec;
vec = temp;
};
void push_front(T data){
int j;
T *temp = new T[size_arr + 1];
for ( j = size_arr; j >= 0;j--) {
temp[j + 1] = vec[j];
}
temp[0] = data;
delete[] vec;
vec = temp;
size_arr++;
};
void insert(int index, T data) {
int j;
T *temp = new T[size_arr + 1];
for (int i = 0; i < size_arr ;i++)
temp[i] = vec[i];
for (int i = 0; i < size_arr;i++) {
if (i == index) {
for ( j = size_arr; j >=i;j--) {
temp[j+1] = vec[j];
}
temp[j + 1] = data;
delete[] vec;
vec = temp;
size_arr++;
}
}
};
void pop() {
T *temp = new T[size_arr - 1];
for (int i = 0; i < size_arr-1;i++)
temp[i] = vec[i];
size_arr--;
delete[] vec;
vec = temp;
};
void Delete(int index)
{
T *temp = new T[size_arr - 1];
for (int i = 0; i < index;i++)
temp[i] = vec[i];
for (int i = 0; i < size_arr;i++) {
if (i == index) {
for (int j = i; j < size_arr-1;j++) {
temp[j] = vec[j + 1];
}
size_arr--;
delete[] vec;
vec = temp;
}
}
};
int search(T data) {
for (int i = 0; i < size_arr;i++) {
if (vec[i] == data) {
return i;
}
}
return -1;
};
int size() { return size_arr; };
};
int main() {
vector <int>test;
test.push_front(2);
test.push_front(3);
test.push_back(0);
test.push_back(-1);
test.insert(2, 2);
test.pop();
vector <int > test1;
test1 = test;// problem
test1.pop();
}
The problem is the line test1 = test;// problem, which does not call the copy constructor, but the assignment operator. You did not declare this operator, so the compiler will use the default implementation, which simply copies all member. So, after the assignment test1.vec and test.vec point to the same memory location.
When you change the line (and the one above it) to vector <int > test1{test};, it will call your copy constructor.
You also forgot to #include <cstring> for memcpy, which you should not use for non-POD types.
You have to multiply the size in memcpy with sizeof(T), because memcpy works on bytes, not on types. You also have to use v.vec instead of v.
Here is the fixed version: https://ideone.com/JMn7ww
I think the problem is in your copy operator. You use memcpy() which is a c function. Which should in itself not be a problem (apart from it being not so nice in many opinions). But since memcpy() is a c function, it doesn't know about types, and it takes its size arguments as a count of bytes.
the element you put in is int which is probably 4 bytes. So when your copy contstructor gets called, and the original has 3 elements, there will be 12 bytes in your array, but malloc will only copy 3 of them.
The comments of other people about not properly copying template types are right, so if you make a vector of strings, you cannot just memcpy them, and assume the result will be new strings. For this answer i was assuming you using only basic types as your template arguments like int, or double.
I just started to implement a basic vector container in C++. It is far from completion, but it looks like this:
using namespace std;
typedef unsigned long long int bigInt;
namespace stl2{
template<class T>
class vector{
private:
bigInt l;
bigInt cap;
T* arr;
public:
vector(){
cap = 0;
l = 0;
}
~vector(){
if (cap > 0) delete[] arr;
}
vector(bigInt size){
cap = size;
l = size;
arr = new T[size];
}
vector(bigInt size, const T& def) : vector(size){
for (bigInt i = 0; i < size; i++){
arr[i] = def;
}
}
bigInt size(){
return l;
}
bigInt length(){
return l;
}
bigInt capacity(){
return cap;
}
void resize(bigInt size){
if (size < cap) return;
l = size;
cap = size;
}
void push_back(const T& data){
// Check if vector is full
if (l == cap) {
//Copy all elements of this vector to another
if (cap == 0)
cap = 1;
else
cap *= 2;
T* temp = new T[cap];
for (int i = 0; i < l; i++){
temp[i] = arr[i];
}
delete[] arr;
arr = temp;
}
arr[l] = data;
l++;
}
//Operators
T& operator[](bigInt index){
if (index < cap)
return arr[index];
}
};
}
So I have a problem with the [] operator. I know that if the index < capacity, I can return arr[index].
But what do I return if the index is greater than or equal to the capacity? Since I am returning a reference to the element, I cannot return a value.
The std::vector throws an exception when using at() with an invalid index, you could do the same.
The operator[] of std::vector however does not perform bounds checking, using an invalid index is undefined behavior.