assignment operator overloading problem - c++

This issue has me confused. The first piece of code works fine without crashing, it assigns s1 to s2 perfectly fine. But the second group of code causes the program to crash.
Anyone have any idea on why this is happening or what the problem could be?
Code 1:(works)
s1.add(10, 30, 25, "Test Screen", false);
s1.add(13, 10, 5, "Name:XXX", false);
s1.add(13, 18, 30);
s1.remove(-1);
Screen s2 = s1;
Code 2:(crashes on assignment)
Screen s1;
if (1 != s1.add(10, 30, 25, "Test Screen", false))
message("first add() has a problem");
else if (2 != s1.add(13, 10, 5, "Name:XXX", false))
message("second add() has a problem");
else if (3 != s1.add(13, 18, 30))
message("third add() has a problem");
else if (3 != s1.remove(-1))
message("first remove() has a problem");
else {
Screen s2 = s1;
}
Assignment operator for screen class:
Screen& operator=(const Screen &scr) {
if (this != &scr){
for (int i = 0; i < 50; i++) {
if (fields[i])
delete fields[i];
fields[i] = new LField();
}
for (int i = 0; i < scr.numOfFields; i++)
fields[i] = scr.fields[i];
numOfFields = scr.numOfFields;
currentField = scr.currentField;
}
return *this;
}
Assignment operator for Field class:
LField& operator=(const LField &lfieldobj) {
if (this != &lfieldobj) {
if (lfieldobj.val) {
if (val)
delete[] val;
val = new char[strlen(lfieldobj.val) + 1];
strcpy(val, lfieldobj.val);
}
else{
//val = new char[1];
val = "";
}
rowNum = lfieldobj.rowNum;
colNum = lfieldobj.colNum;
width = lfieldobj.width;
canEdit = lfieldobj.canEdit;
index = lfieldobj.index;
}
return *this;
}
Any input would be greatly appreciated :)

Get rid of your current val and replace it with an std::string. Get rid of your fields and replace it with an std::vector. That should let you eliminate both of your overloaded assignment operators; the compiler will provide ones that work. I'd guess you'll eliminate the memory management problems along with the code.
As it stands right now, even if you "fix" the memory management problem(s) you know about, you're going to be left with the fact that your code is completely unsafe in the face of exceptions (and uses new so it basically can't avoid exceptions either).

for (int i = 0; i < scr.numOfFields; i++)
fields[i] = scr.fields[i];
That's not okay, you are copying a pointer instead of the pointed-to value. A deep copy is required.

What's the member "field" declaration?
LField* fields[50]?
If so, who's initializing the left hand side object fields member to NULL? I'd say that nobody... assignment operator is like copy constructor in C++, and you're invoking delete on a invalid pointer.

The line
Screen s2 = s1;
actually invokes the Screen copy constructor, not the assignment operator overload.
For example:
#include <iostream>
using namespace std;
class Screen
{
public:
Screen() { }
Screen(const Screen& s)
{
cout << "in `Screen::Screen(const Screen&)`" << endl;
}
Screen& operator=(const Screen& s)
{
cout << "in `Screen::operator=(const Screen&)`" << endl;
return *this;
}
};
int main()
{
Screen s1;
Screen s2 = s1;
}
prints:
in Screen::Screen(const Screen&)
I'm guessing that your Screen copy constructor is defined similar to Screen::operator=(const Screen&), so a fix for the assignment operator overload may need to be applied to the copy constructor definition as well.
Also, how is the fields member of Screen defined? If it is like:
LField* fields[50];
then inside the constructors, you have to initialize all LField* objects in the array to NULL as they have undefined initial values:
std::fill_n(fields, 50, static_cast<LField*>(NULL));
Without this initialization, the test if (fields[i]) could succeed for some i even though fields[i] does not point to an allocation, causing your program to attempt to delete pointer(s) that were not returned by new.

I've manged to fix it. It was a problem with memory allocation after all :)

For complicated objects it is better to use the copy and swap idum.
This gives you an assignment operator with the strong exception gurantee (transactionaly safe). But it also means that you only have to consider the complicated creation of the object in one place (the constructors).
Screen& Screen::operator=(Screen const& rhs)
{
Screen tmp(rhs);
this->swap(tmp);
return *this;
}
void Screen::swap(Screen const& rhs) throw ()
{
// Swap each of the members for this with rhs.
// Use the same pattern for Field.
}

Related

Bad array new length error unhandled exception

I am not sure where I am going wrong with this.
I have a Movie.h with all the data members and constructors destructors and copy constructors needed but I have a feeling it's failing at my assignment operator someone, please help
Movie& Movie::operator=(const Movie& _assign) {
// Self-assignment check
if (this == &_assign)
return *this;
// Shallow copy non-dynamic data members
mRuntime = _assign.mRuntime;
// Deep copy appropriate data members
mTitle = new char[strlen(_assign.mTitle) + 1];
strcpy_s(mTitle, strlen(_assign.mTitle) + 1, _assign.mTitle);
// Deep copy the reviews
SetStars(_assign.mStars, mNumReviews);
return *this;
}
void Movie::SetStars(const int* _stars, int _numReviews) {
// Allocate array and deep copy
mStars = new int[_numReviews];
for (int i = 0; i <= _numReviews; ++i) {
// Cap reviews between 1-10
if (_stars[i] > 10)
{
mStars[i] = 10;
}
else if (_stars[i] < 0)
{
mStars[i] = 0;
}
else
{
mStars[i] = _stars[i];
}
}
// Set the number of reviews
mNumReviews = _numReviews;
}
The problem happens here:
mStars = new int[_numReviews];
for (int i = 0; i <= _numReviews; ++i) {
Specifically here:
i <= _numReview // this causes you to go out of bounds
changing it to:
i < _numReview
resolves the issue
You are allocating _numReview items. C++ has 0-based array indexing. Elements will go from 0 to _numReview - 1
Please consider using std::string and std::vector instead of c-style arrays.

C++ Deep copy of dynamic array through assignment operator

I am trying to copy a dynamically allocated array to an instance. My code seems to be copying the values over, but it also need to resize the array to match the "&other" size array.
A little info about the code: There are two classes at hand, one is "Movie" which takes a title, film-time, and director (all pointers) as private members. There is another called "MovieCollection" which is an array that stores each instance of "Movie" in a given index.
//These are private member variables:`
int ArrySize = 50; //There is another section of code that points to this and resizes if needed, I believe it needed a size at runtime though.
//Array to store instance of "movie"
Movie *movieArry = new Movie[ArrySize];
//This is assignment operator
const MovieCollection& operator=(const MovieCollection& other)
{
delete []movieArray;
int otherSizeArry = other.ArrySize;
Movie* temp;
temp = new Movie[otherSizeArry];
for (int i = 0; i < otherSizeArry; i++)
temp[i] = other.movieArry[i];
return *this;
delete []temp;
}
I used another function I wrote to resize the array while the instance is being created. For example, the instance I want to copy over has 10 indexes but the new instance I am trying to copy the values into still has a limit of 50. From what I understand I have to delete it because arrays cannot be resized, then copy the new size over (along with the values).
Any help would be greatly appreciated and thank you in advanced. Also, sorry if more code is required. I didn't want to give more than what was needed.
Your assignment operator is implemented incorrectly. It is freeing the movieArray array before allocating the new temp array. If the allocation fails, the class will be left in a bad state. And you are not assigning the temp array to movieArray before calling return *this; (the delete []temp is never reached, the compiler should have warned you about that).
The operator should look more like this instead:
MovieCollection& operator=(const MovieCollection& other)
{
if (&other != this)
{
int otherSizeArry = other.ArrySize;
Movie* temp = new Movie[otherSizeArry];
for (int i = 0; i < otherSizeArry; ++i) {
temp[i] = other.movieArry[i];
}
// alternatively:
// std::copy(other.movieArry, other.movieArry + otherSizeArry, temp);
std::swap(movieArray, temp);
ArrySize = otherSizeArry;
delete[] temp;
}
return *this;
}
If your class has a copy constructor (and it should - if it does not, you need to add one), the implementation of the assignment operator can be greatly simplified:
/*
MovieCollection(const MovieCollection& other)
{
ArrySize = other.ArrySize;
movieArray = new Movie[ArrySize];
for (int i = 0; i < ArrySize; ++i) {
movieArray[i] = other.movieArry[i];
}
// alternatively:
// std::copy(other.movieArry, other.movieArry + ArrySize, movieArray);
}
*/
MovieCollection& operator=(const MovieCollection& other)
{
if (&other != this)
{
MovieCollection temp(other);
std::swap(movieArray, temp.movieArray);
std::swap(ArrySize, temp.ArrySize);
}
return *this;
}

Even the Copy Assignment Operator can't assist

Kindly help me figure out where the issue is. I have followed the rule of three as well and made several modifications to the code.
#include <iostream>
using namespace std;
class AStack {
public:
AStack();
AStack(int);
AStack(const AStack&);
~AStack();
AStack& operator = (const AStack& s);
void push(int);
int pop();
int top();
bool isEmpty();
void flush();
private:
int capacity ;
int* a;
int index = -1; // Index of the top most element
};
AStack::AStack() {
a = new int[25];
capacity = 25;
}
AStack::AStack(int size) {
a = new int[size];
capacity = size;
}
AStack::AStack(const AStack& s) {
capacity = s.capacity;
delete[] a; // To avoid memory leak
a = new int[capacity];
for (int i = 0; i < capacity; i++) {
a[i] = s.a[i];
}
index = s.index;
}
AStack::~AStack() {
delete[] a;
}
AStack& AStack::operator = (const AStack& s) {
capacity = s.capacity;
delete[] a; // To avoid memory leak
int* a = new int[capacity];
for (int i = 0; i < capacity; i++) {
a[i] = s.a[i];
}
index = s.index;
return *this;
}
void AStack::push(int x) {
if (index == capacity - 1) {
cout << "\n\nThe stack is full. Couldn't insert " << x << "\n\n";
return;
}
a[++index] = x;
}
int AStack::pop() {
if (index == -1) {
cout << "\n\nNo elements to pop\n\n";
return -1;
}
return a[index--];
}
int AStack::top() {
if (index == -1) {
cout << "\n\nNo elements in the Stack\n\n";
return -1;
}
return a[index];
}
bool AStack::isEmpty() {
return (index == -1);
}
void AStack::flush() {
if (index == -1) {
cout << "\n\nNo elements in the Stack to flush\n\n";
return;
}
cout << "\n\nFlushing the Stack: ";
while (index != -1) {
cout << a[index--] << " ";
}
cout << endl << endl;
}
AStack& reverseStack(AStack& s1) {
AStack s2;
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
s1 = s2;
return s1;
}
int main() {
AStack s1;
s1.push(1);
s1.push(2);
s1.push(3);
s1.push(4);
s1.push(5);
s1 = reverseStack(s1);
cout << "\n\nFlushing s1:\n";
s1.flush();
system("pause");
return 0;
}
I fail to understand how even after defining the appropriate copy assignment operator, the values in s1 after returning from the function are garbage values.
If your copy constructor is correct, and your destructor is correct, your assignment operator could be written in a much easier and safer fashion.
Currently, your assignment operator has two major flaws:
No check for self-assignment.
Changing the state of this before you know you can successfully allocate the
memory (your code is not exception safe).
The reason for your error is that the call to reverseStack returns a reference to the current object. This invoked the assignment operator, thus your assignment operator was assigning the current object to the current object. Thus issue 1 above gets triggered.
You delete yourself, and then you reallocate yourself, but where did you get the values from in the loop to assign? They were deleted, thus they're garbage.
For item 2 above, these lines change this before you allocate memory:
capacity = s.capacity;
delete[] a; // To avoid memory leak
What happens if the call to new[] throws an exception? You've messed up your object by not only changing the capacity value, but you've also destroyed your data in the object by calling delete[] prematurely.
The other issue (which needs to be fixed to use the copy/swap idiom later in the answer), is that your copy constructor is deallocating memory it never allocated:
AStack::AStack(const AStack& s) {
capacity = s.capacity;
delete[] a; // ?? What
Remove the line with the delete[] a, since you are more than likely calling delete[] on a pointer that's pointing to garbage.
Now, to rid you of these issues with the assignment operator, the copy/swap idiom should be used. This requires a working copy constructor and a working destructor before you can utilize this method. That's why we needed to fix your copy constructor first before proceeding.
#include <algorithm>
//...
AStack& AStack::operator = (AStack s)
{
std::swap(capacity, s.capacity);
std::swap(a, s.a);
std::swap(index, s.index);
return *this;
}
Note that we do not need to check for self assignment, as the object that is passed by value is a brand new, temporary object that we are taking the values from, and never the current object's values (again, this is the reason for your original code to fail).
Also, if new[] threw an exception, it would have been thrown on the call to the assignment operator when creating the temporary object that is passed by value. Thus we never get the chance to inadvertently mess up our object because of new[] throwing an exception.
Please read up on what the copy/swap idiom is, and why this is the easiest, safest, and robust way to write an assignment operator. This answer explains in detail what you need to know:
What is the copy-and-swap idiom?
Here is a live example of the fixed code. Note that there are other changes, such as removing the default constructor and making the Attack(int) destructor take a default parameter of 25.
Live Example: http://ideone.com/KbA20D

What is wrong with my park_car function?

I'm again doing a task for school and I'm implementing it slowly, I don't know why my park_car function is not working, I just wanted to make a test and the program crashes ... here is my code.
PS: I can't change the ***p2parkboxes because it is given in the starter file like most other variables. I just want to see the first element of Floor 0 as : HH-AB 1234. Your help is most appreciated.
PS2: I can't use the std::string as well it isn't allowed for the task.
#include <iostream>
#include <cstring>
using namespace std;
#define EMPTY "----------"
class Parkbox{
char *license_plate; // car's license plate
public:
Parkbox(char *s = EMPTY); // CTOR
~Parkbox(); // DTOR
char *get_plate(){return license_plate;}
};
class ParkingGarage{
Parkbox ***p2parkboxes;
//int dimensions_of_parkhouse[3]; // better with rows,columns,floors
int rows,columns,floors; // dimensions of park house
int total_num_of_cars_currently_parked;
int next_free_parking_position[3];
// PRIVATE MEMBER FUNCTION
void find_next_free_parking_position();
public:
ParkingGarage(int row, int col, int flr);// CTOR,[rows][columns][floors]
~ParkingGarage(); // DTOR
bool park_car(char*); // park car with license plate
bool fetch_car(char*); // fetch car with license plate
void show(); // show content of garage floor
// by floor
};
Parkbox::Parkbox(char *s ) { // CTOR
license_plate = new char[strlen(s)+1];
strcpy(license_plate, s);
//cout << "ParkBox CTOR" << endl;
}
Parkbox::~Parkbox() { // DTOR
delete [] license_plate;
//cout << "ParkBox DTOR" << endl;
}
ParkingGarage::ParkingGarage(int row, int col, int flr){
rows = row; columns = col; floors = flr;
p2parkboxes = new Parkbox**[row];
for (int i = 0; i < row; ++i) {
p2parkboxes[i] = new Parkbox*[col];
for (int j = 0; j < col; ++j)
p2parkboxes[i][j] = new Parkbox[flr];
}
}
ParkingGarage::~ParkingGarage(){
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j)
delete [] p2parkboxes[i][j];
delete [] p2parkboxes[i];
}
delete [] p2parkboxes;
}
void ParkingGarage::show(){
int i,j,k;
for (i = 0 ; i < floors; i++){
cout << "Floor" << i << endl;
for (j=0;j<rows;j++){
for (k=0;k<columns;k++){
cout << p2parkboxes[j][k][i].get_plate() << " ";
}
cout << endl;
}
}
}
bool ParkingGarage::park_car(char*s){
p2parkboxes[0][0][0] = Parkbox(s); //test
//p2parkboxes[0][0][0] = s; //test
return true;
}
int main(void) {
// a parking garage with 2 rows, 3 columns and 4 floors
ParkingGarage pg1(2, 3, 4);
pg1.park_car("HH-AB 1234");
/*pg1.park_car("HH-CD 5678");
pg1.park_car("HH-EF 1010");
pg1.park_car("HH-GH 1235");
pg1.park_car("HH-IJ 5676");
pg1.park_car("HH-LM 1017");
pg1.park_car("HH-MN 1111"); */
pg1.show();
/*pg1.fetch_car("HH-CD 5678");
pg1.show();
pg1.fetch_car("HH-IJ 5676");
pg1.show();
pg1.park_car("HH-SK 1087");
pg1.show();
pg1.park_car("SE-AB 1000");
pg1.show();
pg1.park_car("PI-XY 9999");
pg1.show(); */
return 0;
}
You did not declare the copy constructor for the Parkbox class. So, the line
p2parboxes[0][0][0] = Parkbox(s)
creates something (instance of Parkbox with a char* pointer) on the stack (and deletes it almost immediately). To correct this you might define the
Parkbox& operator = Parkbox(const Parkbox& other)
{
license_plate = new char[strlen(other.get_plate())+1];
strcpy(license_plate, other.get_plate());
return *this;
}
Let's see the workflow for the
p2parboxes[0][0][0] = Parkbox(s)
line.
First, the constructor is called and an instance of Parkbox is created on stack (we will call this tmp_Parkbox).
Inside this constructor the license_plate is allocated and let's say it points to 0xDEADBEEF location.
The copying happens (this is obvious because this is the thing that is written in code) and the p2parboxes[0][0][0] now contains the exact copy of tmp_Parkbox.
The scope for tmp_Parkbox now ends and the destructor for tmp_Parkbox is called, where the tmp_Parkbox.license_plate (0xDEADBEEF ptr) is deallocated.
p2parboxes[0][0][0] still contains a "valid" instance of Parkbox and the p2parboxes[0][0][0].license_plate is still 0xDEADBEEF which leads to the undefined behaviour, if any allocation occurs before you call the
cout << p2parboxes[0][0][0].license_plate;
Bottom line: there is nothing wrong with the line itself, the problem is hidden within the implementation details of the '=' operator.
At this point it is really better for you to use the std::string for strings and not the razor-sharp, tricky and explicit C-style direct memory management mixed with the implicit C++ copy/construction semantics. The code would also be better if you use the std::vector for dynamic arrays.
The problem here is that you do not have deep copy assignment semantics. When you assign a temporary Parkbox to the Parkbox in the parking garage, the compiler generated assignment operator makes a shallow copy of the pointer license_plate, leaving both Parkboxes pointing at the same memory location. Then the temporary Parkbox goes out of scope and deletes license_plate. Since the other Parkbox is pointing at the same spot its license_plate gets deleted, too.
There are a couple solutions. One way to solve the problem is to define an assignment operator and a copy constructor that provide proper semantics, i.e. that perform deep copies of the license plate string. The better option, and the one that makes better use of C++, is to use std::strings instead of manually allocated C-strings. I strongly suggest the second approach, though working through the first might be instructive.
From the OP:
I solved the Problem with :
void Parkbox::change_plate(char *s){
delete [] license_plate;
license_plate = new char[strlen(s)+1];
strcpy(license_plate, s);
}

overloaded assignment operator - char pointers not copying correctly

I have a class User that looks like this:
class User
{
private:
char* p_username;
int nProcesses;
struct time
{
int mins;
int secs;
} totalTime;
int longestPID;
char* p_longestPath;
public:
User();
User(const char[],int,int,int,const char[]);
~User();
User operator=(const User&);
// Other functions
};
And the overloaded assignment operator function is:
User User::operator=(const User &u)
{
if (this != &u)
{
delete [] p_username;
delete [] p_longestPath;
p_username = new char[strlen(u.p_username)+1];
strcpy(p_username,u.p_username);
nProcesses = u.nProcesses;
totalTime.mins = u.totalTime.mins;
totalTime.secs = u.totalTime.secs;
longestPID = u.longestPID;
p_longestPath = new char[strlen(u.p_longestPath)+1];
strcpy(p_longestPath,u.p_longestPath);
}
return *this;
}
A sample main program using the assignment operator:
int main()
{
cout << "\n\nProgram\n\n";
User u("Username",20,30,112233,"Pathname"),u2;
u2 = u;
}
When I try to use the assignment operator in the line u2 = u, everything is assigned properly except the dynamic char arrays.
Test output from the end of the operator= function shows that at the end of the assignment itself everything has works perfectly (the usernames and pathnames are correct), however test output from the main function directly after the assignment shows that all of a sudden the char arrays have changed. Suddenly the username of u2 is empty, and the first half of the pathname is garbage.
If at the end of the assignment operator function the username and pathname are perfect, how can they be wrong back in the calling function?
This really has me stumped...
Edit: Here are the constructors
User::User()
{
p_username = 0;
nProcesses = 0;
totalTime.mins = 0;
totalTime.secs = 0;
longestPID = -1;
p_longestPath = 0;
}
User::User(const char UID[],int minutes,int seconds,int PID,const char path[])
{
p_username = new char[strlen(UID)+1];
strcpy(p_username,UID);
nProcesses = 1;
totalTime.mins = minutes;
totalTime.secs = seconds;
longestPID = PID;
p_longestPath = new char[strlen(path)+1];
strcpy(p_longestPath,path);
}
You are returning by value from the assignment function. It is possible that your copy constructor is flawed.
You might want to check out this tutorial here:
http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html
Here is an example from there:
MyClass& MyClass::operator=(const MyClass &rhs) {
// Only do assignment if RHS is a different object from this.
if (this != &rhs) {
... // Deallocate, allocate new space, copy values...
}
return *this;
}