Is an isFull() function for a Dynamic Array Container Class needed? - c++

I'm having a bit of trouble wrapping this around my head; I used the debugger in VS to go through my code. I realized that when I call the insertBack() function in main() the elements aren't inserted since the condition if (!isFull) isn't met--returning false causing the insertion to not happen. I tried removing the condition and got some errors regarding my code trying to insert a number into an invalid portion of the array. While going through this, I started to ask myself is the isFull() function required since a dynamic array can be resized; but, how can it be full if this is the case? I looked a bit into vectors on cpprefrence and didn't find an isFull() member function.
#include <iostream>
template<typename T>
class container
{
template <typename T2>
friend std::ostream& operator<<(std::ostream& out, const container<T2> &cobj);
// Postcondition: contents of the container object cobj is displayed
public:
container();
// Postcondition: an empty container object is created with data members arr set to NULL, n set to -1 and Capacity set to 0
~container();
// Destructor; required as one of the Big-3 (or Big(5) because of the presence of a pointer data member. Default version results in
// memory leak!
// Postcondition: dynamic memory pointed to by arr has been release back to the “heap” and arr set to NULL or nullptr
// In order to see the action, message "destructor called and dynamic memory released!" is displayed
bool isEmpty() const;
// Postcondition: returns true is nothing is stored; returns false otherwise
bool isFull() const;
// Postcondition: returns true if arr array is filled to capacity; returns false otherwise
int size() const;
// Postcondition: returns the size or the number of elements (values) currently stored in the container
int capacity() const;
// Postcondition: returns the current storage capacity of the container
bool insertBack(const T& val);
// Postcondition: if container is not full, newVal is inserted at the end of the array;
// otherwise, double the current capacity followed by the insertion
bool deleteBack();
// Precondition: The array must not be empty
// Postcondition: the last element stored in the array is removed! size of the container is decremented by 1, capacity unchanged
void clear();
// Postcondition: all elements in arr of calling container object are cleared and the dynamic memory is released back to “heap”
private:
void allocate(T* &temp);
// Postcondition: if Capacity = 0, allocate a single location; otherwise the current capacity is doubled
T *arr;
int Capacity; // Note: Capital 'C' as capacity is used as a function name
int n; // size or actual # of values currently stored in the container; n <= SIZE
};
template<typename T2>
std::ostream& operator<<(std::ostream& out, const container<T2> &cobj)
{
std::cout << "Currently it contains " << cobj.size() << " value(s)" << std::endl
<< "Container storage capacity = " << cobj.capacity() << std::endl
<< "The contents of the container:" << std::endl;
if (cobj.isEmpty())
{
std::cout << "*** Container is currently empty!" << std::endl;
}
else
{
for (int i=0; i<cobj.size(); ++i)
{
std::cout << cobj.arr[i];
}
}
return out;
}
template<typename T>
container<T>::container()
{
arr = nullptr;
Capacity = 0;
n = 0;
}
template<typename T>
container<T>::~container()
{
delete arr;
arr = nullptr;
std::cout << "Destructor called! (this line is normally not displayed)" << std::endl;
}
template<typename T>
bool container<T>::isEmpty() const
{
return n==0;
}
template<typename T>
bool container<T>::isFull() const
{
return n==Capacity;
}
template<typename T>
int container<T>::capacity() const
{
return Capacity;
}
template<typename T>
int container<T>::size() const
{
return n;
}
template<typename T>
bool container<T>::insertBack(const T& val)
{
if (!isFull())
{
n++;
arr[n-1] = val;
return true;
}
else
{
return false;
}
}
template<typename T>
bool container<T>::deleteBack()
{
if (!isEmpty())
{
n--;
return true;
}
else
{
return false;
}
}
template<typename T>
void container<T>::clear()
{
if (!isEmpty())
{
n = 0;
return true;
}
else
{
return false;
}
}
template<typename T>
void container<T>::allocate(T* &temp)
{
if (Capacity==0)
{
temp = new T;
}
else
{
return Capacity*2;
}
}
int main()
{
container<int> a1;
std::cout << a1 << std::endl;
std::cout << "Currently, the container object contains 0 element(s) or 0 value(s)" << std::endl;
std::cout << "\nWe now insert 3 values at the back of the array, one at a time:" << std::endl;
const int num = 3;
for (int i=0, c=0; i<=num; ++i, c+=10)
{
a1.insertBack(c);
}
std::cout << a1;
}

I think that having an isFull method does not make sense, since your dynamic container is not limited in capacity. Instead, you can use the size and capacity methods to track the state of the container.

If you want to implement a vector and want to check if size smaller than or equals to capacity, then decide whether to resize it, you can wrapper size > = capacity as a private isFull() function. But I think it makes no sense to set it public.

Related

Problem with malloc and memcpy in array class

I've started writing some code for a List class and I'm lost. I need it for my Arduino project – I can't use STL.
Here's the code.
#include <iostream>
template <typename T>
class List
{
public:
List<typename T>()
{
m_Count = 0;
m_Data = nullptr;
}
~List()
{
free(m_Data);
}
void Push(const T& element)
{
m_Count++;
T* alloc = (T*)malloc(size_t(sizeof(T) * m_Count));
memcpy(alloc, m_Data, m_Count * sizeof(T));
*(m_Data + sizeof(T) * (m_Count - 1)) = element;
}
T* operator [](unsigned int x) const
{
return (m_Data + x * sizeof(T));
}
private:
T* m_Data;
uint64_t m_Count;
};
struct Vertex
{
int x;
int y;
};
int main()
{
List<Vertex> list;
list.Push({ 0, 1 });
list.Push({ 2, 3 });
list.Push({ 4, 5 });
std::cout << list[0]->x << list[1]->x << list[2]->x;
}
The problem lies somewhere in the Push method: When I call memcpy, the program triggers a compiler breakpoint.
The essential problem is in the line *(m_Data + sizeof(T) * (m_Count - 1)) = element;. Here, you are attempting to copy the given element into the old (i.e. pre-existing) m_Data array; this will be nullptr the first time the Push function is called (and one element too small every other time)†.
So, you need to first release the old data (with free(m_Data)), then assign your newly-allocated memory to that pointer (m_Data = alloc), and only then copy element to that array's last element.
However, as others have said, why are you using malloc and free in C++? The code below replaces those calls with new[] and delete[] (although using a std::vector would likely be easier/better/safer, if that were possible).
#include <iostream>
template <typename T>
class List {
public:
List<T>() { // Don't need "typename" here!
m_Count = 0;
m_Data = nullptr;
}
~List() {
delete[] m_Data;
}
void Push(const T& element) {
m_Count++;
T* alloc = new T[m_Count];
for (uint64_t i = 0; i < m_Count - 1; ++i) alloc[i] = m_Data[i]; // Copy old data (if any)
delete[] m_Data; // Release old data
m_Data = alloc; // Assign newly-allocated memory to m_Data
m_Data[m_Count - 1] = element; // Why use pointer arithmetic when you have the [] operator?
}
T* operator [](unsigned int x) const {
return &m_Data[x];
}
private:
T* m_Data;
uint64_t m_Count;
};
struct Vertex {
int x;
int y;
};
int main()
{
List<Vertex> list;
list.Push({ 0, 1 });
list.Push({ 2, 3 });
list.Push({ 4, 5 });
std::cout << list[0]->x << list[1]->x << list[2]->x << std::endl;
std::cout << list[0]->y << list[1]->y << list[2]->y << std::endl;
return 0;
}
I have made a couple of other 'minor improvements' to your code (your operator [] looked very suspicious, as the size of the pointed-to object is inherently taken into account when doing pointer arithmetic); there are others that could be made but that would, IMHO, deviate too far from the code you posted.
† Actually, it will always be nullptr in your code, as you never assign anything else to it.

C++: using deque member functions inside a class stack

I am having trouble with stacking values for a deque object defined in a template class stack. When initializing a class stack object and use the push_back() member function of the STL deque class on the initialized object and read the number of elements inside the stack I get zero, yet the terminal told me it pushed values into it. It looks as follows:
Stack header file
#ifndef STACK_HH
#define STACK_HH
#include <iostream>
#include <deque>
template<class T>
class Stack {
public:
Stack(): s() { // <-- creating an empty deque s
s.size() ;
nitems() ;
empty() ;
}
~Stack() {
nitems() ;
empty() ;
}
//Member functions
int nitems() { return s.size() ; }
bool empty() { return (s.size()==0) ; }
void push_back(T c) {
s[s.size()] = c ;
}
T back() {
return s[s.size()] ;
}
void pop_back() {
if (empty()) {
std::cout << "Stack::pop() Error: stack is empty" << std::endl ;
}
}
private:
std::deque<T> s ;
};
#endif
Main script
#include <iostream>
#include <deque>
#include "Stack.h"
using namespace std ;
// Main
int main() {
int LEN = 10;
Stack<double> s ;
// Write doubles into Stack
for (int i=0 ; i<LEN ; i++) {
cout << "pushing value " << i*i << " in stack s" << endl ;
s.push_back(i*i) ;
}
// Count doubles in fifo
cout << s.nitems() << " value(s) in stack" << endl ;
return 0 ;
}
I was wondering if I should define the deque push_back(), back() and pop_back() functions differently in my Stack header file. Something like void deque < T > ::push_back() and similar for the other ones. But this did not solve it as I already was expecting yet using s.pushback() did not work either.
Any help in solving this problem is kindly appreciated.
EDIT:
Thank you all for the help.
You can indeed use directly the deque methods like that:
template<class T>
class Stack {
public:
Stack(): s() { // <-- creating an empty deque s
}
~Stack() {
}
//Member functions
int nitems() const { return s.size() ; }
bool empty() const { return s.empty() ; }
void push_back(const T& c) {
s.push_back(c) ;
}
const T& back() {
return s.back();
}
void pop_back() {
if (empty()) {
std::cout << "Stack::pop() Error: stack is empty" << std::endl ;
}
s.pop_back();
}
private:
std::deque<T> s ;
};
Why not std::stack?
Assigning to s[s.size()] doesn't add new elements:
Notes
Unlike std::map::operator[], this operator never inserts a new element into the container.
From http://en.cppreference.com/w/cpp/container/deque/operator_at.
Use s.push_back(new_element) to add new element.
Last element is s[s.size() - 1] or, better, s.back().
s.size(), s.size() and empty() in constructor and destructor do nothing.

Segmentation fault pushing strings in a stack

I'm still stuck on an issue that keeps occurring with a class assignment which works with templates and Linked-List-based Stacks. I am constantly getting segmentation faults and I cannot for the life of me see why.
main() asks for a value to determine how many disks to play the game with, which calls set_Discs(), which appends "x"s to a string object tmp_str to represent disks for the game. This string object is then pushed onto the stack rod[0] (the first rod) and then it loops back. I call a cout to print the top stack object each time through the loop as a test and it works, so I can successfully store the string objects. However upon calling the next function print_Game() it does the first iteration of the for-loop (printing the first line) and goes bust again.
int main() {
TowerHanoi game;
size_t disc_in;
//How many discs to be used
cout << "Welcome to Towers of Hanoi!"<< "Please enter how many discs you want to play with: " << endl;
cin >> disc_in;
game.set_Discs(disc_in);
game.print_Game();
//Ask for source and target rod
return EXIT_SUCCESS;
}
The set_Discs() function:
void TowerHanoi::set_Discs(size_t disc) {
//cout << "test: " << rod[0].top() << endl;
discs = disc;
while (disc > 0) {
string tmp_str;
for (size_t i=0; i<disc; i++) {
tmp_str.append("x");
}
disc--;
rod[0].push(tmp_str);
cout << "test: " << rod[0].top() << endl;
}
}
The function print_Game() is a recent logical endeavour so forgive me if it's bad and under-worked. The loop adds spacing, then prints the top string object, then calls pop() (which calls removeFromHead() in LinkedList.template) to make the next string object the top object for the next time through the loop, and so on. Refer to addToHead().
void TowerHanoi::print_Game() {
size_t spaces = discs;
string topLen = rod[0].top();
string xString;
cout << "top length: " << topLen.length() << endl;
for (size_t a=0; a<discs;a++) {
LStack<string> rodCopy = rod[0];
cout << "==="; //3 spaces
for (size_t i=0; i<spaces-topLen.length();i++) { //add to xString
xString.append("=");
}
cout << "===" << xString << rod[0].top() << xString << endl;
rod[0].pop();
spaces--;
}
}
Some relevant code from LStack.template (this is the Stack):
//push
template <typename Obj>
void LStack<Obj>::push(Obj& head_in) {
list->addToHead(head_in);
used++;
}
//pop
template <typename Obj>
Obj LStack<Obj>::pop() {
used--;
return list->removeFromHead();
}
//top
template <typename Obj>
const Obj& LStack<Obj>::top() {
return list->list_getHead();
}
Relevant code from LinkedList.template:
template <typename Item>
void LinkedList<Item>::addToHead(Item& entry) {
node<Item>* temp = head;
head = new node<Item>();
head->set_data(entry);
head->set_link(temp);
}
template <typename Item>
const Item& LinkedList<Item>::removeFromHead() {
node<Item>* head_copy = head; //create a copy of head
head->set_data(head_copy->link()); //set head's data to the previous object
return head_copy->data(); //return head's original data
}
Node.template:
template <typename Object>
void node<Object>::set_data(const Object& new_data){
word = new_data;
}
template <typename Object>
void node<Object>::set_link(node<Object>* new_link){
next= new_link;
}
template <typename Object>
void node<Object>::set_previous(node<Object>* new_prev) {
previous = new_prev;
}
template <typename Object>
const Object& node<Object>::data() const{ //return the word
return word;
}
template <typename Object>
const Object& node<Object>::link() const { //return next node (const function)
return next->data();
}
template <typename Object>
const Object& node<Object>::back() const { //return previous node (const)
return previous->data();
}
Identifying the data being pushed on stack:
void TowerHanoi::set_Discs(size_t disc)
{
while (disc > 0)
{
string tmp_str; <-- note here, tmp_str is stack variable
rod[0].push(tmp_str);
}
}
Now, let us see the LStack::push() function:
//push
template <typename Obj>
void LStack<Obj>::push(Obj& head_in) <-- note here, input to push() is pass by reference
{
list->addToHead(head_in);
used++;
}
Once, set_Discs() function call is done, the reference to the used stack variables inside this function is not valid.
This may be related to the problem of program crashing / terminating abruptly when print_Game() function is called where LStack stored data is being used.

Last function not displaying correct output

I am not getting the expected output for the program that I have coded, I know that the implementation for the last function is incorrect. I don't know how to implement this so that it returns the last entry into the array, and doesn't remove it. The second question that I have is on the pop_back function, its supposed to remove last entry pushed into the vector and reduce it by one, and if it's empty do nothing. The way it is now it just reduces the vector by one. Thanks for your help in advance.
Driver
#include <iostream>
#include "vectorHeader.h"
using namespace std;
int main()
{
const char START = 'A';
const int MAX = 12;
// create a vector of doubles
myVector<char> vectD;
// push some values into the vector
for (int i = 0; i < MAX; i++)
{
vectD.push_back(START + i);
}
// remove the last element
vectD.pop_back();
// add another value
vectD.push_back('Z');
// test memory management
myVector<char> vectD2 = vectD;
// display the contents
cout << "\n[";
for (int i = 0; i < vectD2.size() - 1; i++)
{
cout << vectD2[i] << ", ";
}
cout << "..., " << vectD2.last() << "]\n";
system("PAUSE");
return 0;
}
Header
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
#include <array>
//Declaring constant
const int VECTOR_CAP = 2;
template <class T>
class myVector
{
private:
//Setting data members
T* vectorData;
int cap;
int numElements;
public:
//Default constructor
//Purpose: Creates a vector
//Parameters: None
//Returns: None
myVector();
//Parameterized constructor
//Purpose: Creates a vector capacity of n
//Parameters: None
//Returns: None
myVector(const T&);
//Copy Constructor
//Purpose: Copy data into vector
//Parameters: myVector object
//Returns: None
myVector(const myVector& copy)
{
numElements = copy.numElements;
vectorData = new T [numElements];
for (int i = 0; i < numElements; i++)
{
this->vectorData[i] = copy.vectorData[i];
}
}
//Destructor
//Purpose:Deletes any dynamically allocated storage
//Parameters: None
//Returns: None
~myVector();
//Size function
//Purpose: returns the size of your vector
//Parameters: None
//Returns: The size of your vector as an integer
int size() const;
//Capacity function
//Purpose: Returns the capacity of the vector
//Parameters: None
//Returns: Maximum value that your vector can hold
int capacity() const;
//Clear function
//Purpose: Deletes all of the elements from the vector and resets its size to
// zero and its capacity to two; thus becoming empty
//Parameters: None
//Returns: None
void clear();
//push_back function
//Purpose: Adds the integer value n to the end of the vector
//Parameters: Takes a integer to be placed in the vector
//Returns: None
void push_back(const T& n)
{
//If statement to handle if array is full
if (numElements == cap)
{
//Doubling the capacity
cap = cap * VECTOR_CAP;
//Allocating new array
T* newVectorData = new T[cap];
//Copying data
for (int i = 0; i < numElements; i++) newVectorData[i] = vectorData[i];
//Deleting previous data
delete[] vectorData;
//Pointing to new data
vectorData = newVectorData;
}
//Storing data
vectorData[numElements++] = n;
}
//at function
//Purpose: Returns the value of the element at position n in the vector
//Parameters: None
//Returns: Returns your current place with the vector
T& at(std::size_t);
//assignment
//Purpose: Overload the = operator
//Parameters: The two myVector objects we want to assign
//Returns: The assignment
myVector operator=(const myVector&);
void pop_back();
int last();
T& operator[](std::size_t);
};
//Independant Functions
template <typename T>
myVector<T>::myVector()
{
//Setting the cap
cap = VECTOR_CAP;
//Creating the array
vectorData = new T[cap];
//Initializing the value
numElements = 0;
}
template <typename T>
myVector<T>::~myVector()
{
cap = 0;
//Delete array elements
delete[] vectorData;
//Allocate vectorData
vectorData = NULL;
}
template <typename T>
int myVector<T>::size() const
{
return numElements;
}
template <typename T>
void myVector<T>::pop_back()
{
numElements--;
}
template <typename T>
int myVector<T>::last()
{
return cap;
}
template <typename T>
int myVector<T>::capacity() const
{
return cap;
}
template <typename T>
T& myVector<T>::at(std::size_t n)
{
return vectorData[n];
}
template <typename T>
T& myVector<T>::operator[](std::size_t n)
{
return vectorData[n];
}
template <typename T>
myVector<T> myVector<T>::operator=(const myVector& rho)
{
//Test for assingment
if (this == &rho)
{
return *this;
}
//Delete lho
delete[] this->vectorData;
//Creating new array to fit rho data
cap = rho.cap;
this->vectorData = new int[cap];
//Copying data
for (int i = 0; i < numElements; i++)
{
this->vectorData[i] = rho.vectorData[i];
}
//Returning myVector object
return *this;
}
template <typename T>
std::ostream& operator<<(std::ostream& out, const myVector<T>& rho)
{
for (int n = 0; n < rho.size(); n++)
{
out << rho.at(n);
}
return out;
}
The last function should look like this:
template <typename T>
T myVector<T>::last()
{
return vectorData[numElements - 1];
}
A function that returns an element from the vector should have the return type the type of the vector elements, i.e. T in your case.
cout has different overloads for char and int. For char it prints the ASCII character of the code provided, for int it returns the code itself. So if your last element is Z and you return an int it would print the ASCII code of Z that is 90.
try this to get a sense of what I am saying:
cout << 'Z' << endl;
cout << (char) 'Z' << endl; // tautological cast as 'Z' is char
cout << (int) 'Z' << endl;
cout << 90 << endl;
cout << (int) 90 << endl; // tautological cast as 90 is int
cout << (char) 90 << endl;
As for pop_back all you have to do is:
if (numElements > 0)
numElements--;
Reason: there is no such thing as deleting memory. A cell of memory always has a value (be it one you set, 0, 1 or garbage). All you have to do is mark it as being available (or not used). That's what you do when you shrink numElements.
If you want to go a step further you could do the opposite of push_back, i.e. relocate the whole vector to a smaller allocated buffer. But this is not advised as these operations (allocating, copying) are expensive and you can do without them (unlike pus_back where you have to get a bigger size)

C++: Overloading the << operator

I have a very silly problem that I just can't figure out. I'm trying to overload the << operator in my "PrioQueue" class. I'm still pretty new to C++ and i've tried almost every example I could find on the web but nothing works.
The PrioQueue is a template class I made that works like a normal Queue but puts the highest value it receives on the top.
PrioQueue<int> intq1(5);
intq1.push(1);
intq1.push(2);
intq1.push(1);
cout << intq1;
The << operator should write all the values that I've pushed to the queue with a '|' in between. So like this:
2 | 1 | 1 |
This is my overload operator << method.
friend std::ostream& operator<<(std::ostream& out, PrioQueue q){
while(!q.empty()){
out.write(q.pop()); //This method pops off the top value and returns it
}
return out;
}
I hope this is enough information but if not. This is my full code:
#include "stdafx.h"
#include <iostream>
#include <ostream>
using namespace std;
template <typename Type>
class PrioQueue
{
private:
Type *bottom_;
Type *top_;
int size_;
public:
PrioQueue(Type size){
bottom_ = new Type[size];
top_ = bottom_;
size_ = size;
}
friend PrioQueue operator+(PrioQueue q1, PrioQueue q2){
while(!q2.empty()){
q1.push(q2.pop());
}
return q1;
}
friend std::ostream& operator<<(std::ostream& out, PrioQueue q){
while(!q.empty()){
out.write(q.pop());
}
return out;
}
//Checks to see if the given value is bigger than the bottom character.
//If so, the bottom and the given value swap places.
//If not, the value gets placed at the top of the queue
void push(Type t){
if(*bottom_ < t){
*top_ = *bottom_;
*bottom_ = t;
}else{
*top_ = t;
}
top_++;
}
int num_items() {
return (top_ - bottom_);
}
Type pop(){
return *(bottom_++);
}
int full() {
return (num_items() >= size_);
}
int empty() {
return (num_items() <= 0);
}
void print(){
cout << "Queue currently holds " << num_items() << " items: " ;
for (Type *element=top_-1; element >= bottom_; element--) {
cout << " " << *element;
}
cout << "\n";
}
int getSize(){
return size_;
}
~PrioQueue(){ // stacks when exiting functions
bottom_ = 0;
delete[] bottom_;
}
};
void intExample(){
PrioQueue<int> intq1(5);
intq1.push(1);
intq1.push(2);
intq1.push(1);
cout << intq1;
intq1.print();
PrioQueue<int> intq2(5);
intq2.push(8);
intq2.push(2);
intq2.push(5);
intq2.print();
PrioQueue<int> intq3(10);
intq3 = intq1 + intq2;
intq3.print();
cout << intq3;
}
void charExample(){
PrioQueue<char> charq1(5);
charq1.push('t');
charq1.push('h');
charq1.push('g');
charq1.print();
PrioQueue<char> charq2(5);
charq2.push('i');
charq2.push('q');
charq2.push('k');
charq2.print();
PrioQueue<char> charq3(10);
charq3 = charq1 + charq2;
charq3.print();
}
int main(){
intExample();
charExample();
return 0;
}
This would work:
friend std::ostream& operator<<(std::ostream& out, PrioQueue q){
while(!q.empty()){
out << q.pop() << "|";
}
return out << "\n"; // if you want a newline, otherwise just "return out;"
}
Edit Note that for this to work, you will have to modify your queue to be copyable. You need to add a copy constructor and an assignment operator (see the rule of three) or use a type that is copyable and assignable to store your data (see std::vector, std::deque, or container adapter std::priority_queue if this is not an exercise).
The problem is that the out.write() expects a const char* and size. First of all, your Type will not implicitly convert to const char* and also, you're missing an argument.
Using out << q.pop() will fix the problem for you.