Sorting doesn't work with templated class - c++

I have an insertion sort function
void insertionSort(ArrayList<int> myData)
{
for (int i = 1; i < myData.getSize(); i++) {
int index = myData[i];
int j = i;
while (j > 0 && myData[j-1] > index) {
myData.swap(j - 1, j);
j--;
}
myData[j] = index;
}
}
which uses this swap function
template<class TYPE>
void ArrayList<TYPE>::swap(int from, int to) throw(std::out_of_range)
{
int temp = 0;
temp = this->items[from];
this->items[from] = this->items[to];
this->items[to] = temp;
swapNum++;
}
This is how my private methods look like
TYPE * items;
int currentLength;
static int swapNum;
I have an overloaded [] operator and a getSize() function that I think I wrote well and not contributing to my problem. Now if I do this in my main.cpp
ArrayList<int>m_Data(1);
and append say 4,2,9,1 on the m_Data and call
insertionSort(m_Data);
I get two errors
1. Error C2440 '=': cannot convert from 'std::string' to 'int'
on the swap function and
2. The insertion sort doesn't work

First problem: it should be something like TYPE temp = this->items[from]. After repairing it (I used STL swap) function works. Well, it works on STL vector and swap. If you still do have problem, then your array structure is probably invalid.
EDIT: In function 'insertionSort' shouldn't you have template (as in swap function)?

Related

How can I use one function to sort by different variables in a struct

In my program, I have this struct:
struct Record {
string ID;
string name;
int quantity;
double price;
};
I also have a function that is supposed to bubble sort an array of pointers to Record that looks like this:
void printAscending(Record* pitemList[50], int arraySize) {
int Swap;
Record* Temp;
do{
Swap = 0;
for(int i = 0; i < (arraySize - 1); i++) {
if ((pitemList[i] -> quantity) > (pitemList[i + 1] -> quantity)){
Temp = pitemList[i];
pitemList[i] = pitemList[i + 1];
pitemList[i + 1] = Temp;
Swap = 1;
}
}
} while (Swap != 0);
}
There are four different variables in struct and I have to ask a user to pick one to sort by. I can't create four different functions for each variable, and I also can't copy and paste the same bubble sort code four times for each variable. Is there any way to create one function with one block of bubble sort code that can sort by four different variables?
Not sure I understand what you want, but to generalize your code to use a different member of the records as a comparator, I suppose you can pass pointers to members as template parameters.
You've tagged your question as c++17, so you can use auto for template parameters, so:
template <auto Record::* rMember>
void printAscending (Record* pitemList[50], int arraySize)
{
bool Swap;
do
{
Swap = false;
for (int i = 0; i < (arraySize - 1); i++)
{
if ( pitemList[i]->*rMember > pitemList[i + 1]->*rMember )
{
std::swap(pitemList[i], pitemList[i+1]);
Swap = true;
}
}
}
while ( true == Swap );
}
You can call it as follows:
printAscending<&Record::quantity>(itemList, sizeItemList);
I think it would be better to use std::sort, which lets you write just your compare functions and takes care of the sorting.
You could use these:
#include <algorithm>
bool compareIDs(const Record *r1, const Record *r2) { return r1->ID < r2->ID; }
bool compareNames(const Record *r1, const Record *r2) { return r1->name < r2->name; }
bool compareQuantities(const Record *r1, const Record *r2) { return r1->quantity < r2->quantity; }
bool comparePrices(const Record *r1, const Record *r2) { return r1->price < r2->price; }
// And call sort like this: std::sort(array1, array2, compareIDs);
References:
https://en.cppreference.com/w/cpp/algorithm/sort
https://en.cppreference.com/w/cpp/named_req/Compare
I am writing this from the top of my head but I think that this is the step in the right way.
You would need to have few functions like this:
bool compareByQuantity(const Record &a, const Record &b) {
return a.quantity < b.quantity;
}
You would need to implement this for every property. And then, to sort this, you can provide third argument to sort method ... third argument is function. like this
std::sort(records.begin(), records.end(), compareByQuantity);
Hope this gets you on the right track
Link to sort documentation: https://en.cppreference.com/w/cpp/algorithm/sort
As pointed out by #Ted Klein Bergman you can also use a lambda directly like this
std::sort(records.begin(), records.end(), [](const Record &a, const Record &b){ return a.quantity < b.quantity });

Destructor called after returning from function

I've got some project for my University and I need to perform converting some data from file to matrix representation.
The main problem is that after returning form "returnNeighbours(int node)" destructor is called on neighbours object(as I concluded from running gdb).
I know that destructor is always called when local variable, in function, is initialized, but neihbours is a class member. I won't post everything, because it's not necessary I think. I've got some structures listed below.
representation.cpp
NodeContainer::NodeContainer(){ size = 0; array = nullptr; }
NodeContainer::~NodeContainer(){ size = 0; delete[] array; }
void NodeContainer::allocateMemoryAndSetSize(int n){ size = n; array = new int[size]; }
void MatrixRep::convertDataToMatrixRep(int** array)
{
for(int i = 0 ; i != size; i++)
for(int j = 0; j != size; j++)
matrix[i][j] = array[i][j];
}
NodeContainer MatrixRep::returnNeighbours(int node)
{
deleteNeighboursIfAny();
if(!checkIfNotBeyondMatrix(node))
return neighbours;
neighbours.allocateMemoryAndSetSize(countNeighbours(node));
for(int i = 0, j = 0; i < size; i++)
if(matrix[node-1][i] != 0)
{
neighbours.array[j] = matrix[node-1][i];
j++;
}
return neighbours;
}
void MatrixRep::deleteNeighboursIfAny(){ if(neighbours.array) neighbours.~NodeContainer(); }
bool MatrixRep::checkIfNotBeyondMatrix(int node)
{
if(node == 0 || node > size)
{
std::cerr<<"There is no such a node!\n";
return false;
}
else
return true;
}
int MatrixRep::countNeighbours(int node)
{
int count_non_zero = 0;
for(int i = 0; i != size; i++)
if(matrix[node-1][i] != 0)
count_non_zero++;
return count_non_zero;
}
representation.h
struct NodeContainer
{
int size;
int* array;
NodeContainer();
~NodeContainer();
void allocateMemoryAndSetSize(int);
};
class MatrixRep
{
int size;
NodeContainer neighbours;
int** matrix;
public:
MatrixRep(int);
~MatrixRep();
void convertDataToMatrixRep(int**);
NodeContainer returnNeighbours(int);
void deleteNeighboursIfAny();
bool checkIfNotBeyondMatrix(int);
int countNeighbours(int);
void setupMatrix();
void deleteMatrix();
};
If you would like to return a copy of NodeContainer, you must implement a copy constructor and assignment operator for it. If you're using a C++11 conformant compiler it will also be good to also implement a move constructor and move assignment operator as well.
On the other hand, if you would like to not create a copy, you must either return a pointer or a reference to the member. You could also make the member a std::shared_ptr, which you may return in this case.
However, in your current implementation you're actually returning a shallow copy of NodeContainer. Once your copy goes out of scope its destructor is called, which deallocates its memory, which in this case is the original memory of your member, effectively making your member invalid. The implementation is not good as it is. So, depending on your goal, either implement the first advised solution, or the second.

Template array of pointers to objects of child classes as argument for a function

I have a few classes that inherit the same class with a print method. I also have a custom-made dynamic array template class. I have created a few dynamic arrays of pointers to objects from the child classes. For every array I want to have a separate function for calling all the print methods of the objects pointed by the pointers- some times I want to print only the "weapons", sometimes only the "modifications" or sometimes everything. So far I have tried two solutions- copy-pasting the first method (as shown in the code) for every array or converting the dynamic arrays into arrays of pointers to the "mother" class and passing the new ones as an argument to an universal print function.
And here is some code:
class Item {...}
class Modification : public Item {...}
class Equipment : public Item {...}
DynamicArray<Modification*> modification;
DynamicArray<Equipment*> weapon;
//The first way:
void printModsInfo ()
{
if (modification.size() == 0)
cout<<"No mods in inventory\n";
else
for (int i = 0; i < modification.size(); i++)
modification.returnElement(i)->printInfo();
}
void printWeaponsInfo ()
{
if (weapon.size() == 0)
cout<<"No weapons in inventory\n";
else
for (int i = 0; i < weapon.size(); i++)
weapon.returnElement(i)->printInfo();
}
//The second way:
void _printModsInfo ()
{
Item** tempRef = new Item*[modification.size()];//array of pointers
for (int i = 0; i < modification.size(); i++)//converting DynamicArray<Modification*> into Item** tempRef
tempRef[i] = modification.returnElement(i);
printCertainStuffInInventory (tempRef, modification.size());
delete tempRef;
}
void _printWeaponsInfo ()
{
Item** tempRef = new Item*[weapon.size()];
for (int i = 0; i < weapon.size(); i++)
tempRef[i] = weapon.returnElement(i);
printCertainStuffInInventory (tempRef, weapon.size());
delete tempRef;
}
void printCertainStuff (Item** arr, int size)
{
if (size == 0)
cout<<"Nothing from this type in inventory...\n";
else
for (int i = 0; i < size; i++)
arr[i]->printInfo();
}
So I have two choices: copy-paste the five rows from the fist way, or copy-paste the more complicated five rows from the second way and add five more rows for the printing function. But what I really want to do is to simply pass the dynamic arrays as arguments and make the conversion (if needed) in the printing function- or to simply call the "printer" by writing: printCertainStuff(modification); (or "weapon" or whatever). And this is required by the design of the whole project. I did consult my teacher, but the answer was that there is no way to do that without converting before calling the function.
But still- is there a way to pass such dynamic arrays as arguments the way I want?
I'm not 100% clear on what you're wanting, but if it is to combine all your print methods you could try using a template:
template< class T >
void printInfo ( const T& arr, const char* failmessage )
{
if (arr.size() == 0)
cout<<failmessage;
else
for (int i = 0; i < arr.size(); i++)
arr.returnElement(i)->printInfo();
}
And then to use for weapons you'd go:
printInfo( weapon, "No weapons in inventory\n" );
And similarly for modifications.

Why does this code generate error?

I have a class that contains an array of another class called Sphere. Right now i am not sure why one part of code is generating an error.
.H file
sphere* list;
int listlen;
void add(sphere x);
sarray operator +(const sarray& arrayone);
.Cpp
sarray::sarray()
{
listlen = 0;
list = new sphere[200000];
}
sarray::~sarray()
{
delete [] this->list;
}
void sarray::add(sphere x) // Function for adding spheres to the list.
{
listlen++;
list[listlen-1] = x;
}
void sarray::operator = (const sarray& arrayone)
{
this -> list = NULL;
for(int i = 0; i < arrayone.listlen; i++)
{
this -> add(arrayone.list[i]);
}
}
sarray sarray::operator +(const sarray& arrayone)
{
sarray temparray;
for(int i = 0; i < arrayone.listlen; i++) // add all the elements from the first array to the temporary one
{
//sphere temp = arrayone.list[i];
temparray.add(arrayone.list[i]);
}
for(int j = 0; j < this -> listlen; j++)// add all the elements from the second array to the temporary one
{
temparray.add(list[j]);
}
return temparray;
}
The sphere class got a member variable called "Radius"
which when i try to compare like this
float a = 10;
for(int i=0; i > this->listlen;i++)
if(this->list[i].Radius > a) //<-- Can read the values
Works fine, but when change this part of the code
float a = 10;
sarray temparray = arrayone + *this;
for(int i = 0; i < temparray.listlen; i++)
if(temparray.list[i].radius > a) // Error comes here!
"Unhandled exception at 0x00138503: Access violation reading location"
while this doesent. I guess the problem is in the Add/operator function but i cant find it.
The following part looks problematic:
void sarray::add(sphere x) // Function for adding spheres to the list.
{
list[listlen-1] = x;
}
you should rather have something like this
void sarray::add(sphere x) // Function for adding spheres to the list.
{
list[listlen++] = x;
}
Also you should better have some error checking in add method.
OK, having looked at the destructor, you have a pointer to sphere in your sarray and has a destructor to destroy the pointer. This is all good except you haven't defined your own copy constructor which means the default copy constructor is used. In the function operator+ where you return temparray, a copy of the local copy is returned. The default copy constructor is called to create the copy. Then the local one will be destructed. Now the returned sarray copy's list will point to invalid data. You need to define your own copy constructor to make a deep copy of the list pointer.

cannot convert from 'std::string' to 'char'

Changed completely due to suggestions from other member. Most problems solved, still having problems. Now won't output any names from the array in main. Not sure if I'm passing them back correctly from function.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void bubblesort(string[], const int);
int sub = 0;
int main()
{
const int maxsize = 100;
string friendArray[maxsize];
ifstream friends;
friends.open("myFriends.dat");
while (sub < maxsize)
{
getline(friends, friendArray[sub]);
sub++;
}
bubblesort(friendArray, maxsize);
cout<<friendArray[0]<<" "<<friendArray[1]<<" "<<friendArray[2];
system("pause");
return 0;
}
void bubblesort(string *array, const int size)
{
bool swap;
string temp;
do
{
swap = false;
for (int count = 1; count < (size - 1); count++)
{
if(array[count-1] >array[count])
{
temp = array[count-1];
array[count-1] = array[count];
array[count] = temp;
swap = true;
}
}
}
while(swap);
}
Your problem isn't necessarily that temp inside bubblesort is not a char, the problem is that array is declared as a string and not a string[].
The reason you're getting the error is because array[count+1] is of type char, and temp is of type string. std::swap expects two elements of the same type.
However, that may be the least of your problems, your code doesn't compile for quite a few reasons. Not just that but you're passing in maxsize to bubblesort at each iteration. There's a flaw in both your logic and your syntax.
EDIT: Since you're still having trouble getting the sorting to work, here's a working modification of your code:
#include <iostream>
void bubblesort(std::string array[], size_t size)
{
bool bSwapped;
std::string temp;
do
{
bSwapped = false;
for (size_t count = 1; count < size; count++)
{
if(array[count-1] > array[count])
{
std::swap(array[count-1], array[count]);
bSwapped = true;
}
}
}
while(bSwapped);
}
int main(void)
{
std::string array[] = { "def", "ghk", "abc", "world", "hello" };
bubblesort(array, sizeof(array)/sizeof(*array));
for (size_t i = 0; i < sizeof(array)/sizeof(*array); ++i)
std::cout << array[i] + " ";
std::cout << std::endl;
return 0;
}
bubblesort could also be written as: void bubblesort(std::string *array, size_t size). There's no difference in this case since, when passed to a function, arrays decay into pointers.
Since arrays are passed by reference, a pointer to the first element, any modifications made to array inside of bubblesort will actually be modifying your array in main. So that's how arrays are "returned".
std::vector is a good alternative to the standard array, since it automatically resizes and obviously contains the length of the internal array so that you don't have to pass the size everywhere you pass an std::vector. You can also use it the same way as a regular array.
temp is a string, array[count] is a char (since an std::string is a vector of char elements.) I'm not sure what you're trying to do here, but the compiler is correct - you can't assign a char to a string.
You could change temp to be a char, since all you do with it is assign a char to it, and then assign it back to an element of array, which is also a char.
You need to declare temp as char. You can use std::swap to avoid such mistakes in the future:
std::swap(array[count], array[count+1]);
This would make your code compile, but it would not do what you're trying to do (bubblesort). The problem is that you are passing a single string (which is also an "array" of characters) instead of an array of strings, which is, in a very lose sense, "an array of arrays of characters". Your bubblesort needs to accept string *array as its first parameter.