I'm having a little trouble with the following:
I'm writing a map abstract data type for some coursework & I've come across a problem whilst trying to assign an object of my class (MapEntry - below) to an array of the same type in the class MapADT. It tells me that:
Error 1 error C2679: binary '=' : no operator found which takes a right-hand operand of type 'MapEntry *' (or there is no acceptable conversion) c:\users\cross_000\documents\visual studio 2013\projects\objectorientedmethodsasignment1\mapadt\mapadt.h 14
So I thought I would write my own Assignment operator override. I've done this in the MapEntry class definition but the compiler doesn't seem to recognize it when I try to initialize the array in the constructor on MapADT - below.
Any help would be greatly appreciated.
#pragma once
template <class Tk, class Tc>
class MapEntry
{
private:
Tk key;
Tc contents;
bool isPopulated;
public:
MapEntry() {
}
MapEntry(Tk keyInput, Tc contentsInput) {
key = keyInput;
contents = contentsInput;
isPopulated = true;
}
MapEntry(Tk keyInput, Tc contentsInput, bool isPopulatedInput) {
key = keyInput;
contents = contentsInput;
isPopulated = isPopulatedInput;
}
~MapEntry() {
//TODO
}
Tk getKey() {
return key;
}
void setKey(Tk keyInput) {
key = keyInput;
}
Tc getContents() {
return contents;
}
void setContents(Tc contentsInput) {
contents = contentsInput;
}
bool getIsPopulated() {
return isPopulated;
}
void setIsPopulated(bool isPopulatedInput) {
isPopulated = isPopulatedInput;
}
MapEntry<Tk, Tc>& operator=(const MapEntry<Tk, Tc> & lst)
{
clear();
copy(lst);
return *this;
}
};
MapADT.h
#pragma once
#include "MapEntry.h"
template <class Tk, class Tc>
class MapADT
{
private:
int mapSize = 1000;
MapEntry<Tk, Tc> *map;
public:
MapADT() {
map = new MapEntry<Tk, Tc>[mapSize];
for (int i = 0; i < mapSize; i++) {
map[i] = new MapEntry<Tk, Tc>(NULL, NULL, false);
}
}
}
There's more to the MapADT class but I don't think it's relevant. If you need to see the whole thing I can add it.
map[i] is not an pointer to MapEntry.
Not sure if you want
map[i] = MapEntry<Tk, Tc>(NULL, NULL, false);
or
MapEntry<Tk, Tc>** map;
MapADT() {
map = new MapEntry<Tk, Tc>*[mapSize];
for (int i = 0; i < mapSize; i++) {
map[i] = new MapEntry<Tk, Tc>(NULL, NULL, false);
}
}
to solve your issue.
In this line
map = new MapEntry<Tk, Tc>[mapSize];
you allocate an array of MapEntry<Tk, Tc>, and default constructors are called for all of them. There's no need in subsequent for loop at all, you should just write proper initialization in MapEntry::MapEntry(), which is currently empty.
Related
I am testing with a custom list (array) class and, generally, it works well.
It works when I use code like this:
List<int> list;
list = {1,2,3,4,5,6};
for(auto x : list) {
cout << x << endl;
}
However, when I try the following I get an error:
List<int> list = {1,2,3,4,5,6};
This will take the list's items as parameters of a constructor and give me error. How I can make the operator= function work when creating the constructor?
Here is my code:
#include <iostream>
using namespace std;
template <typename T>
class List {
private:
unsigned int l_size = 0;
T* list = new T[l_size];
public:
List(unsigned int size = 0) {
resize(size);
}
void resize(unsigned int size)
{
List<T> old_list = *this;
l_size = size;
list = new T[l_size];
for(int i = 0; i < old_list.size(); i++) {
list[i] = old_list[i];
}
}
T& operator[](int pos) {
return list[pos];
}
template <unsigned int N>
void operator=(const T(&list)[N]) {
resize(N);
for(int i = 0; i < N; i++) {
this->list[i] = list[i];
}
}
unsigned int size() {
return l_size;
}
T* begin() {
return &list[0];
}
T* end() {
return &list[l_size];
}
};
int main() {
List<int> list;
list = {1,2,3,4,4,5};
for(auto x : list) {
cout << x << endl;
}
/* The output is
1
2
3
4
4
5
This works fine.
*/
List<int> list2 = {0,9,8,7,6,4};
//This is taking the list elements a parameters of constructor so when i type like this it will give me an error. how i can make the operator= works when creating the constructor?
return 0;
}
When a declaration statement has an initializer (as in your List<int> list2 = { 0,9,8,7,6,4 }; line), it is not considered an assignment but, rather, calls the relevant constructor for the type (if there is one).1
Your List class doesn't have a constructor that takes a std::initializer_list argument, so you need to add one. Something like this:
List(const std::initializer_list<T>& list) {
size_t N = list.size(), i = 0;
resize(N);
for (auto& data: list) this->list[i++] = data;
}
(You'll also need a #include <initializer_list> line in your source.)
1 Your List<int> list2 = { 0,9,8,7,6,4 }; is equivalent to the more explicit constructor-based syntax, List<int> list2{ 0,9,8,7,6,4 }; (dropping the = token); many modern C++ programmers have a strong preference for the latter.
I have a assignment where I'm suppose to build template using these specifications.
ISet is a container that holds values of a certain where order doesn't matter and
which does not allow duplicates (or multiples).
A dynamically allocated array of type T should be used as an internal data structure for the Set.
The Set should inherit from the ISet interface below - this must not be modified:
template <typename T>
class ISet
{
public:
virtual bool insert (T element) = 0;
virtual bool remove (T element) = 0;
virtual int size () const = 0;
};
• insert (T element): adds elements to the set and returns true provided that
the element is not already present in the quantity (in which case the element is not added and false is returned).
• remove (T element): removes elements from the set and returns true.
If the element is missing in the quantity, false returns.
• size (): returns the number of elements in the set.
In addition to the member functions, you must implement constructor, destructor, copy constructor
and assignment operator.
And so far have I come up with this code:
#pragma once
#include <string>
#include <iostream>
using namespace std;
template <class T>
class ISet
{
public:
virtual bool insert(T element) = 0;
virtual bool remove(T element) = 0;
virtual int size() const = 0;
};
#pragma once
#include "ISet.h"
template <class T>
class Set : public ISet<T>
{
public:
Set(string name);
~Set();
Set(const Set &origin);
//Set& operator=(const Set &origin);
bool insert(T element);
bool remove(T element);
int size()const;
private:
string name;
T *arr;
int cap, nrOfElement;
};
template<class T>
Set<T>::Set(string name)
{
this->name = name;
this->cap = 10;
this->nrOfElement = 0;
this->arr = new T[this->cap];
}
template<class T>
Set<T>::~Set()
{
delete[] arr;
}
template<class T>
Set<T>::Set(const Set & origin)
{
this->nrOfElement = origin.nrOfElement;
this->cap = origin.cap;
arr = new T*[cap];
for (int i = 0; i < nrOfElement; i++)
{
arr[i] = origin.arr[i];
}
}
template<class T>
bool Set<T>::insert(T element)
{
bool found = false;
if (nrOfElement == 0)
{
this->arr[0] = element;
this->nrOfElement++;
}
else
{
for (int i = 0; i < this->nrOfElement; i++)
{
if (this->arr[i] == element)
{
i = this->nrOfElement;
found = true;
}
}
if (found == false)
{
this->arr[nrOfElement++] = element;
}
}
return found;
}
template<class T>
bool Set<T>::remove(T element)
{
bool removed = false;
for (int i = 0; i < this->nrOfElement; i++)
{
if (this->arr[i] == element)
{
this->arr[i] = this->arr[nrOfElement];
nrOfElement--;
removed = true;
}
}
return removed;
}
template<class T>
int Set<T>::size() const
{
return this->nrOfElement;
}
And my problems starts when I start to test this code by adding the different data-type we are suppose to test the template against.
#include "Set.h"
#include "ISet.h"
#include "Runner.h"
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
Set<string> test("test");
test.insert("lol");
cout << test.size();
test.remove("lol");
cout << test.size();
Set<Runner> test2("test");
getchar();
return 0;
}
Getting the error saying that "No operator found which takes a left-hand operand type of 'Runner'. So I have to create a operator== that handles this but don't know?
Runner class looks like this:
#pragma once
#include "Competitor.h"
#include <string>
using namespace std;
class Runner : public Competitor
{
public:
Runner();
Runner(string firstName, string lastName, int startNr);
~Runner();
void addResult(int resultTime);
int getResult() const;
string toString() const;
Runner *clone() const;
private:
int resultTime;
};
#include "Runner.h"
Runner::Runner()
{
this->resultTime = 0;
}
Runner::Runner(string firstName, string lastName, int startNr) : Competitor(firstName, lastName, startNr)
{
this->resultTime = 0;
}
Runner::~Runner()
{
}
void Runner::addResult(int resultTime)
{
this->resultTime = resultTime;
}
int Runner::getResult() const
{
return this->resultTime;
}
string Runner::toString() const
{
return (to_string(this->resultTime) + " sec");
}
Runner * Runner::clone() const
{
return new Runner(*this);
}
How do I build a operator== that will work for this?
You need to add operator== to the Runner class:
bool operator==(const Runner& other) const;
I'm looking for an efficient way to check if a POD variable is altered between two cycles. I've come up with this solution:
class Foo {
public:
template<typename T>
bool isChanged(T& entry);
void endCycle();
private:
std::map<void*,size_t> entryMap; // <Address orig.,Size>
std::map<void*,void*>oldVals; // <Address orig., Address cpy.>
};
template<typename T> bool Foo::isChanged(T& entry)
{
entryMap[&entry] = sizeof(T);
if(oldVals[&entry] == NULL)
return false;
if(memcmp(&entry, oldVals[&entry], entryMap[&entry]))
return true;
else
return false;
}
void Foo::endCycle()
{
// Copy all the bytes to save them for the next cycle
for( std::map<void*,size_t>::iterator entryIt = entryMap.begin();
entryIt != entryMap.end();
++entryIt)
{
if(oldVals[entryIt->first] == NULL)
oldVals[entryIt->first] = malloc(entryIt->second);
memcpy(oldVals[entryIt->first], entryIt->first, entryIt->second);
}
}
Now i can use it like this:
Foo gBar;
void aFunction()
{
int ar;
char ba[3][3];
// Some code where ar and ba are filled
if(gBar.isChanged(ar))
// Do Something
if(gBar.isChanged(ba))
// Do Something
gBar.endCycle();
}
Is this an efficient way? My goal was a method which is very easy to use inside various cyclically called functions. I cleaned all the init and free logic from the code. Any suggestions? I especially don't like the oldshool malloc, memcpy and memcmp stuff but i don't know any other way how to do it.
Edit: Found a good solution based on Red Alerts suggestions.
I think you can use templates a little more effectively here.
template <typename T>
class Foo
{
public:
static std::map<T*, T> values;
static bool isChanged(T& entry)
{
auto it = values.find(&entry);
if(it == values.end())
{
values[&entry] = entry;
}
else if(entry != it->second)
{
it->second = entry;
return true;
}
return false;
}
};
template <typename T>
std::map<T*, T> Foo<T>::values;
int main() {
int ar = 3;
cout << Foo<int>::isChanged(ar) << endl; // 0
ar = 4;
cout << Foo<int>::isChanged(ar) << endl; // 1
for(auto& value : Foo<int>::values)
cout << value.second << endl; // 4
return 0;
}
This way you get one map per type, and you don't have to worry about inadvertently messing up an alias. You do need to define operator != and have a working copy constructor for your types, but that is much better than blindly using memcmp and memcpy.
You can also make further template specializations for arrays if you need to compare those (will be a bit more code, but nothing very complicated)
Edit: To get you started, this is what your template signature should look like:
template<class T, size_t N> bool isChanged(T(&entry)[N]); //will be called for stack allocated arrays
Or you can use char* to alias all of your values. This will let you use a single map for everything (like you were doing before, but this has no memcpy/memcmp). It will only work for POD. We could manually call the destructor when overwriting the buffer, but since there is no good way to do this in the class's destructor, it's probably best to leave out heap allocated data altogether.
class Foo
{
std::map<char**, char*> values;
public:
~Foo()
{
for(auto& value : values)
{
delete[] value.second;
}
}
template<typename T> bool isChanged(T& entry)
{
char** addr = reinterpret_cast<char**>(&entry);
auto it = values.find(addr);
if(it == values.end())
{
alignas(T) char* oldBuf = new char[sizeof(T)];
T* oldEntry = new(oldBuf) T;
*oldEntry = entry;
values[addr] = oldBuf;
}
else if(entry != *(reinterpret_cast<T*>(it->second)))
{
T* oldEntry = new(it->second) T;
*oldEntry = entry;
return true;
}
return false;
}
};
After many hours i think i found a good solution. The call stays easy and there are no casts. It's a lot more complex than the C-Style version with memcopy but I think its nicer and has also the benefit that it works with complex data not just POD.
class Manager
{
public:
~Manager()
{
funcPtrs.clear();
}
void adFnc(void(*function)())
{
funcPtrs.push_back(function);
}
void runAll()
{
for(auto& val : funcPtrs)
val();
}
private:
std::vector<void (*)()> funcPtrs;
};
Manager gAllClearManager;
template<typename T>
class Data
{
public:
Data()
{
gAllClearManager.adFnc(clearValues);
}
static void clearValues()
{
values.clear();
}
static std::map<T*,std::vector<T>>& getValues() { return values; }
private:
static std::map<T*,std::vector<T>> values;
};
template <typename T>
static bool isChanged(T& entry)
{
const static Data<T>* dataP = new Data<T>();
static std::map<T*,std::vector<T>>& values = dataP->getValues();
auto it = values.find(&entry);
if(it == values.end())
{
values[&entry].push_back(entry);
}
else if(entry != it->second[0])
{
it->second[0] = entry;
return true;
}
return false;
}
template<typename T, size_t N>
bool isChanged(T (&entry)[N])
{
const static Data<T>* dataP = new Data<T>();
static std::map<T*,std::vector<T>>& values = dataP->getValues();
auto it = values.find(entry);
if( it == values.end())
{
for(int i = 0; i < N ; ++i )
values[entry].push_back(entry[i]);
return false;
}
else
{
for(int i = 0; i < N ; ++i )
{
if(it->second[i] != entry[i])
{
for(int j = 0; j < N ; ++j )
{
it->second[j] = entry[j];
}
return true;
}
}
}
return false;
}
template<typename T>
std::map<T*, std::vector<T>> Data<T>::values;
Now i can use it like:
int main() {
int ar;
std::string ba[6];
if(isChange(ar))
// Do something
if(isChange(ba))
// Do something
}
My first template is finally working! :) Thanks again Red Alert.
I am wondering how to sort an array that contains objects of a custom class. I am trying to apply different sorting algorithms but in the swapping something goes wrong.
Here is my Code:
class RaceCar
{
private:
char* _brand;
char* _model;
double _price;
int _horse_power;
public:
//Other code
RaceCar(const RaceCar& rc):_price(rc._price), _horse_power(rc._horse_power)
{
_brand = new char[strlen(rc._brand)+1];
strcpy(_brand, rc._brand);
_model = new char[strlen(rc._model)+1];
strcpy(_model,rc._model);
}
RaceCar& operator=(const RaceCar& rc)
{
if(this != &rc)
{
delete _brand;
delete _model;
_brand = new char[strlen(rc._brand)+1];
strcpy(_brand, rc._brand);
_model = new char[strlen(rc._model)+1];
strcpy(_model, rc._model);
_price = rc._price;
_horse_power = rc._horse_power;
}
return *this;
}
bool operator<(const RaceCar& rc)
{
return (this->_price/this->_horse_power) > (rc._price/rc._horse_power);
}
//Other code
};
And this is the class that contains an array of RaceCars. I am trying to implement SortCars() method that orders the RaceCar objects inside the array of cars:
class RaceCarGarage
{
private:
RaceCar* _cars;
int _max_cars;
int _curr_occupied;
public:
RaceCarGarage():_cars(NULL), _max_cars(0),_curr_occupied(0){}
RaceCarGarage(const RaceCar& car, int max_cars)
:_max_cars(max_cars), _curr_occupied(0)
{
_cars = new RaceCar[_max_cars];
}
~RaceCarGarage()
{
delete _cars;
}
void AddCar(const RaceCar& car)
{
if(_curr_occupied < _max_cars)
{
_cars[_curr_occupied] = car;
_curr_occupied += 1;
}
}
void DisplayCars()
{
if(_curr_occupied > 0)
{
for(int i=0 ; i<_curr_occupied ; i++)
{
cout<<(i+1)<<". ";
(_cars+i)->Display();
}
}
}
void SortCars()
{
if(_curr_occupied > 1)
{
for(int i=0 ; i<_curr_occupied ; i++)
{
for(int j = i+1 ; j<_curr_occupied ; j++)
{
if(_cars[j]<_cars[i])
{
RaceCar buffer = _cars[i];
_cars[i] = _cars[j];
_cars[j] = buffer;
}
}
}
}
}
};
The problem with the swapping is that you use the traditional way to do:
temp = a // operator=
a = b // operator=
b = temp; // operator=
However, if you write:
RaceCar temp = a; // Copy constructor gets called (see comment on standard)
a = b; // operator=
b = temp; // operator=
The default copy constructor, just copies member by member, so just copies your pointer. So at the end, your temp and your will try to delete twice the same object pointed to.
Remark on assignment initializer :
For a type T, a statement in form T a = b; is an initializer.
The ISO standard C++ in section 12.6.1 point 1 explains "a single assignment-expression can be specified as an initializer using the = form of initialization. Either direct-initialization semantics or copy-initialization semantics apply;"
I am trying to create custom array indexed from 1 using subscript operator. Getting value works fine, but I have no clue, why assign using subscript operator doesn't work.
class CEntry {
public:
CKey key;
CValue val;
CEntry(const CKey& key, const CValue& val) {
this->key = key;
this->val = val;
}
CEntry& operator= (const CEntry& b) {
*this = b;
return *this;
};
};
...
class EntriesArray {
public:
CEntry **entries;
int length;
EntriesArray(int length) {
this->length = length;
entries = new CEntry*[length];
int i;
for (i = 0; i < length + 1; i++) {
entries[i] = NULL;
}
};
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return *entries[index - 1];
};
};
Constructs array this way
EntriesArray a(5);
This works
a.entries[0] = new CEntry(CKey(1), CValue(1));
cout << a[1].val.value << endl;
This doesn't work
a[1] = new CEntry(CKey(1), CValue(1));
EDIT:
Using
CEntry *operator=( CEntry *orig)
it compiles okey, but gdb stops at
No memory available to program now: unsafe to call malloc warning: Unable to restore previously selected frame
with backtrace
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5f3ffff8
0x00000001000013c8 in CEntry::operator= (this=0x0, orig=0x1001008d0) at /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp:20
20 /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp: No such file or directory.
in /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp
At first... This:
CEntry& operator= (const CEntry& b) {
*this = b;
return *this;
};
Shouldn't work (this should result in recursive call of operator=).
The second thing is that you're trying to assign CEntry * to CEntry, this would work if you had CEntry *operator=( CEntry *orig), but I think this is bad coding practice.
This question may be related to this one.
I tried to fix your code; I believe that this is what you were trying to do:
(tested this code on g++ 5.3.0)
#include <iostream>
#include <stdexcept>
#include <string>
// Some implementation for CKey and CValue:
typedef int CKey;
struct CValue {
int value;
CValue(int value=0) : value(value) {}
};
class CEntry {
public:
CKey key;
CValue val;
CEntry(): key(0), val(0) {}
CEntry(const CKey& key, const CValue& val): key(key), val(val) {}
CEntry& operator= (const CEntry& b) {
this->key = b.key;
this->val = b.val;
return *this;
};
};
class EntriesArray {
public:
CEntry *entries;
int length;
EntriesArray(int length) {
this->length = length;
entries = new CEntry[length];
};
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw std::domain_error("out of bounds!");
}
return entries[index - 1];
};
};
int main(int argc, char* argv[]) {
using namespace std;
EntriesArray a(5);
// This works
a.entries[0] = CEntry(CKey(1), CValue(1));
cout << a[1].val.value << endl;
// This doesn't work
a[1] = CEntry(CKey(2), CValue(2));
cout << a[1].val.value << endl;
}
Also you might want to use a[1] as a[1].val.value e.g.:
cout << a[1] << endl;
To do this just add to this line to cEntry:
operator int() { return val.value; }
I hope it helps.
You could try replacing
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return *entries[index - 1];
};
with
void Add(const int index, CEntry *pEntry) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
entries[index - 1] = pEntry;
};
but since you are now storing references to objects allocated on the heap (with new) you will need a destructor ~EntriesArray() to delete them all.
Because EntriesArray::operator[] returns a CEntry &, but new CEntry returns a CEntry *.
Perhaps you want a[1] = CEntry(CKey(1), CValue(1))? (no new.)
By the way, your current definition of CEntry::operator= will lead to a stack overflow.
This
return *entries[index - 1];
dereferences a NULL pointer.
You want the pointer itself to be overwritten by a[1] = new CEntry(CKey(1), CValue(1));, not the pointed-to-value.
Try this:
class EntriesArray
{
public:
int length;
CEntry **entries;
EntriesArray( int length ) : length(length), entries(new CEntry*[length]())
{
}
// defaulted special member functions are inappropriate for this class
EntriesArray( const EntriesArray& ); // need custom copy-constructor
~EntriesArray(); // need custom destructor
EntriesArray& operator=(const EntriesArray&); // need custom assignment-operator
CEntry*& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return entries[index - 1];
}
};
Further to my comment above:
To make it work with writing new values, you probably need something like this
(I haven't double checked for off by one or ptr vs reference stuff)
CEntry& operator[] (const int index) {
if (index < 1) {
throw ArrayOutOfBounds();
}
// Add default elements between the current end of the list and the
// non existent entry we just selected.
//
for(int i = length; i < index; i++)
{
// BUG is here.
// We don't actually know how "entries" was allocated, so we can't
// assume we can just add to it.
// We'd need to try to resize entries before coming into this loop.
// (anyone remember realloc()? ;-)
entries[i] = new CEntry();
}
return *entries[index - 1];
};