I am learning about dynamic allocation of memory in C++. I've come across a problem for which I can't seem to find an answer.
In my program, I have a struct which goes like this:
struct Friend
{
string name = "";
int daysSinceContact = 0;
};
struct User
{
string name = "";
string password = "";
int numberOfFriends = 0;
Friend *friends = new Friend[numberOfFriends];
};
In my program, I create an array of users, which goes something like this:
int numberOfUsers = 5;
User *usersInformation = new User[numberOfUsers];
And it works fine. But I'd like to be able to add more friends to a chosen user, for example:
int nFriends = usersInformation[0].numberOfFriends;
usersInformation[0].numberOfFriends++;
usersInformation[0].friends[nFriends-1].name = "John";
usersInformation[0].friends[nFriends-1].daysSinceContact = 2;
I'm guessing that I should use a buffer to copy the information from the array containing information about friends, and do something like this:
delete[] usersInformation[0].friends[];
usersInformation[0].numberOfFriends++;
usersInformation[0].friends = new Friend[numberOfFriends];
And then copy it back and add information about the new friend. But when I've tried, it didn't work.
Do you have any tips?
The correct solution is to not use a manual array at all, use the STL's std::vector container instead, eg:
#include <vector>
#include <string>
struct Friend
{
std::string name = "";
int daysSinceContact = 0;
};
struct User
{
std::string name = "";
std::string password = "";
std::vector<Friend> friends;
};
...
std::vector<User> usersInformation(5);
...
Friend newFriend;
newFriend.name = "John";
newFriend.daysSinceContact = 2;
usersInformation[0].friends.push_back(newFriend);
// Or simpler:
usersInformation[0].friends.emplace_back(Friend{"John", 2});
That being said, if you really want to manage the array manually, you need to do something more like this instead (which includes implementing the Rule of Five to prevent corrupting and leaking memory):
#include <string>
struct Friend
{
std::string name = "";
int daysSinceContact = 0;
};
struct User
{
std::string name = "";
std::string password = "";
int numberOfFriends = 0;
Friend *friends = new Friend[numberOfFriends];
// default constructor (nothing extra that isn't already done above)
User() = default;
// copy constructor
User(const User &src) :
name(src.name),
password(src.password),
numberOfFriends(src.numberOfFriends),
friends(new Friend[numberOfFriends])
{
for(int i = 0; i < numberOfFriends; ++i)
friends[i] = src.friends[i];
}
// move constructor
User(User &&src) :
name(std::move(src.name)),
password(std::move(src.password)),
numberOfFriends(numberOfFriends),
friends(src.friends)
{
src.friends = nullptr;
src.numberOfFriends = 0;
}
// destructor
~User()
{
delete[] friends;
}
// copy assignment operator
User& operator=(const User &src)
{
if (this != &src)
{
Friend *newFriends = new Friend[src.numberOfFriends];
for(int i = 0; i < src.numberOfFriends; ++i)
newFriends[i] = src.friends[i];
name = src.name;
password = src.password;
delete[] friends;
friends = newFriends;
numberOfFriends = src.numberOfFriends;
}
return *this;
}
// move assignment operator
User& operator=(User &&src)
{
name := std::move(src.name);
password = std::move(src.password);
Friend *oldFriends = friends;
friends = src.friends;
src.friends = oldFriends;
int oldNumber = numberOfFriends;
numberOfFriends = src.numberOfFriends;
src.numberOfFriends = oldNumber;
return *this;
}
// addition helper
void addFriend(const std::string &name, int daysSinceContact = 0)
{
Friend *newFriends = new Friend[numberOfFriends + 1];
for(int i < 0; i < numberOfFriends; ++i)
newFriends[i] = friends[i];
newFriends[numberOfFriends].name = name;
newFriends[numberOfFriends].daysSinceContact = daysSinceContact;
delete[] friends;
friends = newFriends;
++numberOfFriends;
}
};
Or this, which is slightly safer in terms of memory management:
#include <string>
#include <utility>
#include <algorithm>
struct Friend
{
std::string name = "";
int daysSinceContact = 0;
};
struct User
{
std::string name = "";
std::string password = "";
int numberOfFriends = 0;
Friend *friends = new Friend[numberOfFriends];
// default constructor (nothing extra that isn't already done above)
User() = default;
// initializing constructor
User(int initialCapacity) :
friends(new Friend[initialCapacity])
{
}
// copy constructor
User(const User &src) :
User(src.numberOfFriends),
name(src.name),
password(src.password),
numberOfFriends(src.numberOfFriends)
{
std::copy(src.friends, src.friends + src.numberOfFriends, friends);
}
// move constructor
User(User &&src) :
name(std::move(src.name)),
password(std::move(src.password)),
numberOfFriends(0),
friends(nullptr)
{
std::swap(friends, src.friends);
std::swap(numberOfFriends, src.numberOfFriends);
}
// destructor
~User()
{
delete[] friends;
}
// copy assignment operator
User& operator=(const User &src)
{
if (this != &src)
User(src).swap(*this);
return *this;
}
// move assignment operator
User& operator=(User &&src)
{
src.swap(*this);
return *this;
}
// swap helper
void swap(User &other)
{
std::swap(name, other.name);
std::swap(password, other.password);
std::swap(numberOfFriends, other.numberOfFriends);
std::swap(friends, other.friends);
}
// addition helper
void addFriend(const std::string &name, int daysSinceContact = 0)
{
User temp(numberOfFriends + 1);
std::copy(friends, friends + numberOfFriends, temp.friends);
temp.friends[numberOfFriends] = Friend{name, daysSinceContact};
std::swap(friends, temp.friends);
++numberOfFriends;
}
};
Either way, then you can do this:
User *usersInformation = new User[5];
...
usersInformation[0].addFriend("John", 2);
...
delete[] usersInformation;
Related
I am doing a custom string class in C++. However, when I debugged my code, the system said that:
Error E0415:no suitable constructor exists to convert from "const char" to "string"
Here is my header file where my custom string class is defined:
#ifndef _STRING
#define _STRING
#include <iostream>
class string {
private:
char* s = nullptr;
unsigned int size = 0;
public:
string();
~string() { delete s; };
void operator=(const char*);
friend std::ostream& operator<<(std::ostream&, string&);
};
#endif
string::string()
: s{ nullptr }
{
s = new char[1];
s[0] = '\0';
}
void string::operator=(const char* source)
{
if (source == nullptr) {
s = new char[1];
s[0] = '\0';
}
else {
size = strlen(source) + 1;
s = new char[size];
for (int k = 1; k < (strlen(source) + 1); k++) {
s[k] = source[k];
}
}
}
std::ostream& operator<<(std::ostream& output, string& result)
{
output << result.s;
return output;
}
And here is my main file which I tried to comply:
#include "custom_string.h"
int main()
{
string a;
a = "testfile";
std::cout << a;
system("pause");
return 1;
}
As you can see, I have declared a constructor to convert const char to my custom string by overloading assignment operator. However, there should be something wrong in my code and I could not find out it. Please help me and thank you
Thanks everyone, I have done to fix it. As it turned out, I have to declare one more constructor to covert between my custom string and const char. That is something like this:
string::string(const string& t){}
string& string::operator=(const char&source){}
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.
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 get a segmentation fault when i try to insert into my map.
The function looks something like this:
void add(std::string id, std::string name)
{
Asset asset(nullptr, false, name);
mAssets.insert(std::make_pair<std::string, Asset>(id,asset)); <-- This line gives segfault
}
mAssets is simply declared
std::map<assetID, Asset> mAssets;
And the Asset class is (sloppy) declared like this:
class Asset
{
public:
Asset(T* a, bool l, std::string f) : asset(a), loaded(l), filename(f)
{
}
Asset(const Asset& copy)
{
loaded = copy.loaded;
filename = copy.filename;
asset = new T();
*asset = *copy.asset;
}
~Asset()
{
delete asset;
}
Asset& operator=(const Asset& other)
{
Asset temp(other);
loaded = temp.loaded;
filename = temp.filename;
std::swap(asset,temp.asset);
return *this;
}
T* asset;
bool loaded;
std::string filename;
};
Your problem is here in your copy constructor:
asset = new T();
*asset = *copy.asset;
I will leave it to you to work out why...
On your copy constructor you are derefferencing a null pointer:
*asset = *copy.asset
from
Asset asset(nullptr, false, name);
Verify your pointers asigments and avoid dereferencing null pointers:
Asset(const Asset& copy)
{
loaded = copy.loaded;
filename = copy.filename;
if (copy.asset)
{
asset = new T(); // better may be asset = new T(copy)
*asset = *copy.asset;
}
else
{
asset = nullptr
}
}
*asset = *copy.asset; //you should check whether asset is NULL or not then check for asset
This is how your code will work:
#include <iostream>
#include <map>
using namespace std;
template<class T>
class Asset
{
public:
Asset(T* a, bool l, std::string f) : asset(a), loaded(l), filename(f)
{
}
Asset(const Asset& copy)
{
cout<<"copy"<<endl;
loaded = copy.loaded;
filename = copy.filename;
asset = new T();
if(© != NULL)
{
if(copy.asset != NULL)
*asset = *(copy.asset);
}
}
~Asset()
{
delete asset;
}
Asset& operator=(const Asset& other)
{
Asset temp(other);
loaded = temp.loaded;
filename = temp.filename;
std::swap(asset,temp.asset);
return *this;
}
T* asset;
bool loaded;
std::string filename;
};
std::map <string,Asset<int> > mAssets;
void add(std::string id, std::string name)
{
Asset<int> asset(NULL, false, name);
mAssets.insert(std::make_pair<std::string, Asset<int> >(id,asset)); //<-- This line gives segfault
}
int main()
{
add("1","hi");
cout<<"run"<<endl;
}
I'm having the above issue when I attempt to set a string (stored in a class) equal to another string. I have combed and combed trying to find if I did not initialize any variables, but I can't find such a situation. In debug mod, I get the above error. In release mode it hangs and Win7 looks for a problem, no major abort or retry window. Here's the relevant code, there is another header file with my main program if you feel it should be included, I'll include the line that causes errors. Language is C++ obviously.
//Error occurs in this area:
Car one;
one = two;
one.addExtra ("Windows");
log << "Car one: " << one << endl;
two = Car(one); // call copy constructor.
//I realize when I call the first one = two, there are no extras
//stored int Car one, which is what differs between the two. Remaining
//code. Extras header:
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
class Extras
{
public:
friend class Car;
friend int main();
friend ostream& operator << (ostream& os, const Extras& in);
friend class CarLot;
Extras(const Extras& other);
Extras& operator=(Extras &rhs);
Extras(string in);
Extras();
~Extras();
void modify_ext(string* in);
//string ex_list;
private:
int place;
string *ex_list;
};
//Extras.cpp:
#include "Extras.h"
Extras::Extras(string in)
{
delete ex_list;
ex_list = new string;
place = 0;
//ex_list = new string[4];
(*ex_list) = in;
place++;
}
Extras::Extras()
{
//ex_list = new string[4];
place = 0;
//for(int i = 0; i < 4; i++)
ex_list = new string;
*ex_list = "0";
}
//Overloaded << operator for Extras class to
//easily output array contents
ostream& operator<< (ostream& os, Extras const &in)
{
os << *(in.ex_list);
return os;
}
Extras& Extras::operator=(Extras &rhs)
{
if(this != &rhs)
{
//string temp;
//temp = rhs.ex_list;
modify_ext(rhs.ex_list);
cout << endl << endl << ex_list << endl << endl;
place = rhs.place;
}
return *this;
}
Extras::Extras(const Extras& other) : place(other.place), ex_list(other.ex_list)
{
//for(int i = 0; i < 4; i++)
//ex_list = other.ex_list;
}
void Extras::modify_ext(string* in)
{
delete ex_list;
ex_list = new string;
(*ex_list).resize((*in).size());
for(unsigned int i = 0; i < (*in).size(); i++)
ex_list[i] = in[i];
}
Extras::~Extras()
{
delete ex_list;
place = 0;
}
//Car Header:
#include "Extras.h"
class Car
{
public:
friend class Extras;
friend Extras& Extras::operator=(Extras &rhs);
friend int main();
friend ostream& operator<< (ostream& os, const Car& in);
friend class CarLot;
friend void add_extra();
~Car();
Car();
Car(Car& other);
Car(string in_name, int in_year, string in_color, float in_cost);
Car& operator=(Car const &rhs);
void edit_extr(int in);
void addExtra(string in);
private:
string name, color;
int year, extr_num;
float cost;
Extras *extr;
};
//Car.cpp:
#include "car.h"
//Constructor
Car::Car(string in_name, int in_year, string in_color, float in_cost)
{
name = in_name;
color = in_color;
year = in_year;
cost = in_cost;
extr = new Extras[3];
extr_num = 0;
}
//Overloaded = operator
Car& Car::operator=(Car const &rhs)
{
if(this != &rhs)
{
name = rhs.name;
color = rhs.color;
year = rhs.year;
cost = rhs.cost;
//delete extr;
extr = rhs.extr;
extr_num = rhs.extr_num;
}
return *this;
}
//Default Constructor
Car::Car()
{
name = "TEMP";
color = "BLUE";
year = 0;
cost = 0;
extr = new Extras[3];
extr_num = 0;
}
//Destructor
Car::~Car()
{
delete extr;
extr = NULL;
}
//Copy constructor
Car::Car(Car& other) : name(other.name), color(other.color), year(other.year),
cost(other.cost), extr_num(other.extr_num)
{
//delete extr;
for(int i = 0; i < extr_num; i++)
{
extr[i].modify_ext(other.extr[i].ex_list);
extr[i].place = other.extr[i].place;
}
}
//Overloaded << operator for Car class
ostream& operator<< (ostream& os, const Car& in)
{
os.precision(2);
os << in.name << ", " << in.year << ", "
<< in.color << ", $"<< in.cost << ", ";
os << "extras include: ";
for(int k = 0; k < in.extr_num; k++)
{
os << in.extr[k] << ", ";
}
os << endl;
return os;
}
void Car::edit_extr(int in)
{
Extras* temp;
temp = new Extras[in];
for(int i = 0; i < in; i++)
temp[i] = extr[i];
extr_num = in;
delete extr;
extr = temp;
}
void Car::addExtra(string in)
{
if(extr_num == 3)
{
//log << "Car has too many extras.";
return;
}
//edit_extr(extr_num + 1);
*(extr[extr_num].ex_list) = in;
extr[extr_num].place++;
extr_num++;
}
As I said, I have one more additional header, another class, and a main program if those need to be included, but I figured this was more than enough code (sorry!) for anyone to look through. Any help would be incredibly appreciated.
What I'm seeing that's broken:
two = Car(one); // call copy constructor.
No, it creates a temporary object with the copy constructor, passes this to operator=() on two, then destroys the temporary.
Extras& operator=(Extras &rhs);
should be:
Extras& operator=(const Extras &rhs);
Extras::Extras(string in)
{
delete ex_list;
ex_list = new string;
place = 0;
//ex_list = new string[4];
(*ex_list) = in;
place++;
}
Better:
Extras::Extras(const string& in): place(1), ex_list(new string(in))
{
}
Extras::Extras(const Extras& other) : place(other.place), ex_list(other.ex_list)
{
//for(int i = 0; i < 4; i++)
//ex_list = other.ex_list;
}
Looking at your default constructor, it's clear that the Extras object owns the string in ex_list. However, this copy constructor claims ownership of the original object's ex_list. It should make its own copy:
Extras::Extras(const Extras& other): place(other.place),
ex_list(new string(other.ex_list))
{
}
void Extras::modify_ext(string* in)
{
delete ex_list;
ex_list = new string;
(*ex_list).resize((*in).size());
for(unsigned int i = 0; i < (*in).size(); i++)
ex_list[i] = in[i];
}
You're copying the string. All you need is:
void Extras::modify_ext(const string* in)
{
*ex_list = *in;
}
Moving on to the Car...
friend class Extras;
friend Extras& Extras::operator=(Extras &rhs);
friend int main();
friend ostream& operator<< (ostream& os, const Car& in);
friend class CarLot;
friend void add_extra();
You should consider refactoring the code to get rid of these.
Car(Car& other);
Car(string in_name, int in_year, string in_color, float in_cost);
Should be:
Car(const Car& other);
Car(const string& in_name, int in_year, const string& in_color, float in_cost);
References are your friends when passing objects to functions.
I'm going to stop here.
In your constructor you are deleting ex_list. It hasn't even been allocated yet, so this is wrong. Remove that and do this instead when allocating your new string:
ex_list = new string(in);
This way you use the string copy constructor. You can get rid of the rest of whatever you were trying to do below in the constructor since this will do it for you.
Edit:
Actually, there are a ton of problems all throughout this code. Is there any reason you want your string to be a pointer internally? You aren't using pointers correctly in a bunch of different places. I only noticed the first one as I scrolled down.
The allocator in debug builds with VC++ uses some magic values to fill allocated areas. In particular, 0xCC is memory that had been allocated but it now freed. So the address 0xCCCCCCD0, looks suspiciously like a small offset (e.g., to a struct or class member) from a pointer in memory that was freed. Given that, Mark Loeser's answer looks promising.