overloaded assignment operator - char pointers not copying correctly - c++

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;
}

Related

How to make a copy constructor?

I want to make a copy constructor for this class RNA to copy the details form another object RNA
#include "RNA.h"
#include"Sequence.h"
#include<bits/stdc++.h>
using namespace std;
RNA::RNA()
{
set_sequence();
}
RNA::RNA(char * seq, RNA_Type atype)
{
int x;
int i=0;
while(1)
{
if(seq[i] != 'C'&&seq[i] != 'G'&&seq[i] != 'A'&&seq[i] != 'U')break;
x++;
i++;
}
x--;
length = x;
this->seq = new char[length];
for(int i=0;i<length;i++)
{
this->seq[i] = seq[i];
}
type = atype;
}
this is the copy constructor
RNA::RNA( RNA& rhs)
{
seq = new char[length];
for(int i=0;i<length;i++)
{
seq[i] = rhs.seq[i];
}
type = rhs.type;
}
in the main I try to do it and it make error
int l;
cin>>l;
char* arr = new char[l];
for(int i=0;i<l;i++)
{
cin>>arr[i];
}
cin>>l;
RNA anas(arr,(RNA_Type)l);
int s;
cin>>s;
char* arr2 = new char[s];
for(int i=0;i<s;i++)
{
cin>>arr2[i];
}
cin>>s;
RNA saeed(arr2,(RNA_Type)s);
saeed(anas); error is here
saeed.Print();
The error is " No match for call to '(RNA) (RNA&)'
so what can i do to solve this error
The simplest way is to let the compiler do it for you.
class RNA
{
RNA_Type type;
std::string seq;
public:
RNA(std::string = /* default seq */, RNA_Type = /* default type */);
/* implicitly generated correctly
~RNA();
RNA(const RNA &);
RNA & operator = (const RNA &);
RNA(RNA &&);
RNA & operator = (RNA &&);
*/
// other members
};
RNA::RNA(std::string aseq, RNA_Type atype)
: seq(aseq.begin(), aseq.find_first_not_of("ACGT")), type(atype)
{}
saeed already exists at that point, and you're trying to use it as if it were a function.
You can't copy-construct except when you are constructing, and it looks like any other initialisation:
RNA saeed(anas);
or:
RNA saeed{anas};
If you want to replace the value of an already existing object, you use assignment (and you need to implement that assignment; read about The Rule Of Three.)
The line
saeed(anas);
is not a call to the constructor RNA::RNA(RNA const&), but to RNA::operator()(RNA const&), which does not exist.
What you presumably want is a copy assignement operator
RNA& RNA::operator=(RNA const&);
which for simple enough classes will be auto-generated by the compiler (as explained in Caleth's answer). To call it, replace your line with
saeed = anas;

error while destructor called

I have a class that has two field one of them is a pointer and another is int value to hold length of string sets in constructor.
class MyString
{
char* m_pchString;
int m_nLength;
public:
MyString(const char* pchString="")
{
m_nLength = strlen(pchString) + 1;
m_pchString = new char(m_nLength);
strcpy_s(m_pchString,m_nLength, pchString);
}
MyString(const MyString &Source)
{
m_nLength = Source.m_nLength;
if (Source.m_pchString)
{
m_pchString = new char(m_nLength);
strcpy_s(m_pchString,m_nLength,Source.m_pchString);
}
else
{
m_pchString = 0;
}
}
~MyString()
{
delete[] m_pchString;
m_pchString = 0;
}
char* GetString()
{
return m_pchString;
}
int GetLength()
{
return m_nLength;
}
};
Then use it in console application and create an object cHello .afterward create another object an assign it by cHello within a block
int main()
{
MyString cHello ("Hello,World");
{
MyString cCopy = cHello;
}
std::cout << cHello.GetString();
}
When the lifetime of cCopy ends, the destructor of cCopy gives me an error. What is the problem in this code?
The expression m_pchString = new char(m_nLength) allocates one character, and initializes it to the value m_nLength. It does not allocate an array of m_nLength elements.
That means you will go way out of bounds when copying the string into the memory pointed to by m_pchString, and you will have undefined behavior.
If you want to allocate an array or more than one elements you need to use square brackets [], as in
m_pchString = new char[m_nLength];

attempting to reference a deleted function - ostream

I am trying to add to my class the output operator << but in compiling (VS2013) I have a message:
"error C2280:
'std::basic_ostream<char,std::char_traits<char>>::basic_ostream(const
std::basic_ostream<char,std::char_traits<char>> &)' : attempting to
reference a deleted function".
here is my code:
#include "Client.h"
Client::Client(MyString id, MyString full_name, char gender, unsigned short age, unsigned short hobbies_num, char** hobbies_list)
{
this->id = id;
this->full_name = full_name;
if (gender == 'm' || gender == 'M')
this->gender = 'M';
else if (gender == 'f' || gender == 'F')
this->gender = 'F';
else
cout << "wrong gender value" << endl;
if (age >= 18)
this->age = age;
else
cout << "wrong age value" << endl;
this->hobbies_num = hobbies_num;
this->hobbies_list = hobbies_list;
}
Client::Client(const Client& other)
{
this->id = other.id;
this->full_name = other.full_name;
this->gender = other.gender;
this->age = other.age;
this->hobbies_num = other.hobbies_num;
this->hobbies_list = other.hobbies_list;
}
Client::~Client()
{
for (int i = 0; i < hobbies_num; i++) // deleting 2 dimension array
delete[] hobbies_list[i];
delete[] hobbies_list;
}
Client& Client::operator = (const Client& other)
{
if (this->id == other.id) //checks if the client is not the same client
return *this;
else
{
for (int i = 0; i < hobbies_num; i++) // deleting 2 dimension array
delete[] hobbies_list[i];
delete[] hobbies_list;
return Client(other);
}
}
ostream& operator << (ostream& cout, const Client& for_print)
{
return cout << for_print.id << endl
<< for_print.full_name << endl
<< for_print.gender << endl
<< for_print.age << endl
<< for_print.hobbies_num << endl;
}
The message is on the line stating at return cout.
here are the prototypes:
#include "MyString.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
class Client
{
MyString id;
MyString full_name;
char gender;
unsigned short age;
unsigned short hobbies_num;
char ** hobbies_list;
public:
Client(MyString, MyString, char, unsigned short, unsigned short, char**);
Client(const Client& other);
~Client(); //dtor
Client& operator = (const Client&); //=
friend ostream& operator << (ostream&, const Client& for_print);
};
I didn't find any solution online. The same command works for me in another class at the same solution.
You don't post the exact place of the compiler error, but I'll try to guess it. Suppose you try to do the following in your code (I won't try to use any C++11 construct to be portable):
char *hobbies[] = {
"hobbie1",
"hobbie2",
/* ... */
};
...
Client a("cl1", "joseph", 'm', 20, hobbies, sizeof hobbies/sizeof hobbies[0]);
/* object declaration with initialization, constructor is called for a */
and then
a = Client("cl2", "john", 'm', 22, hobbies, sizeof hobbies/sizeof hobbies[0]);
/* assignment statemement, constructor is being called for right expression
* and then assignment operator is called to assign it to a.
* Equivalent to:
* a.Client::operator=(Client("cl2",...));
* a new object is created by the compiler, passed as parameter and destroyed
* when the call to operator= is finished, before assigning to a.
*/
In this last statement, the compiler tries to construct a new object (for the expression evaluation) and assign it to a, using operator Client::operator=(const Client&other); with parameter other instantiated to this new created object (the compiler just deallocates it, calling the destructor, when the statement finishes, you can check this putting tracing code in the ~Client() body). Just when you return the value Client(other) (in the final return statement of the Client::operator= body), you instantiate a new expression of type Client with values copied with something that will be deleted by the compiler when the expression evaluation finishes (either the client created to pass it to the assignment operator or the one you construct for the return statement) They cease to exist when the return statement is just executed, so you cannot access them outside the operator call (I think this is the error you get from the compiler). You had better to rewrite the Client::operator=(const Client& other) this way:
Client& Client::operator=(const Client& other)
{
if (this == &other) /* the same object in both sides */
return *this;
else {
/* delete local memory assignated
* **in constructor** to object, not elsewhere
*/
/* do the actual assignments onto *this,
* don't create another instance or value. This
* is an assignment operation, you are supposed to
* change the values of *this
*/
return *this;
/* **never** return a local copy made by the compiler
* inside the function call, it ceases to exist just
* on returning from it */
} /* if */
} /* Client::operator=(const Client&) */
I don't believe this same example compiles ok in another environment, it's wrong C++ construct.
You can also see how I have called the constructor with static char arrays (not strings) so they must not be deallocated in the destructor of Client (to illustrate that you might better not deallocate memory in the destructor if it has been allocated elsewhere).

strlen() not working

Basically, I'm passing a pointer to a character string into my constructor, which in turn initializes its base constructor when passing the string value in. For some reason strlen() is not working, so it does not go into the right if statement. I have checked to make sure that there is a value in the variable and there is.
Here is my code, I've taken out all the irrelevant parts:
Label class contents:
Label(int row, int column, const char *s, int length = 0) : LField(row, column, length, s, false)
{
}
Label (const Label &obj) : LField(obj)\
{
}
~Label()
{
}
Field *clone() const
{
return new Label(*this);
}
LField class contents:
LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = "", bool canEditVal = true)
{
if(strlen(valVal) > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Field *clone() const
{
return new LField(*this);
}
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
strcpy(val, clone.val);
rowNum = clone.rowNum;
colNum = clone.colNum;
width = clone.width;
canEdit = clone.canEdit;
index = clone.index;
}
Screen class contents:
class Screen {
Field *fields[50];
int numOfFields;
int currentField;
public:
Screen()
{
numOfFields = 0;
currentField = 0;
for(int i = 0; i < 50; i++)
fields[i] = NULL;
}
~Screen()
{
for (int i = 0; i < 50; i++)
delete[] fields[i];
}
int add(const Field &obj)
{
int returnVal = 0;
if (currentField < 50)
{
delete[] fields[currentField];
fields[currentField] = obj.clone();
numOfFields += 1;
currentField += 1;
returnVal = numOfFields;
}
return returnVal;
}
Screen& operator+=(const Field &obj)
{
int temp = 0;
temp = add(obj);
return *this;
}
};
Main:
int main () {
Screen s1;
s1 += Label(3, 3, "SFields:");
}
Hopefully someone is able to see if I am doing something wrong.
<LANGUAGE FEATURE XXXX IS BROKEN>! ... No, it isn't.
Just before measuring the string, write in a puts(valVal), to ensure you are not mistaken about the contents of that variable.
Marcin at this point the problem will come down to debugging, I copied your code with some minor omissions and got the correct result.
Now it needs to be said, you should be using more C++ idiomatic code. For instance you should be using std::string instead of const char* and std::vector instead of your raw arrays.
Here is an example of what the LField constructor would look like with std::string:
#include <string> // header for string
LField(int rowNumVal,
int colNumVal,
int widthVal,
const std::string& valVal = "",
bool canEditVal = true)
{
std::cout << valVal;
if(valVal.length() > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
//val = NULL;
}
}
Using these types will make your life considerably easier and if you make the change it may just fix your problem too.
PREVIOUS:
So you can be CERTAIN that the string is not being passed in correctly add a printline just before the strlen call. Once you do this work backward with printlines until you find where the string is not being set. This is a basic debugging technique.
Label(int row,
int column,
const char *s,
int length = 0) :
LField(row, column, length, s, false) {
}
LField(int rowNumVal,
int colNumVal,
int widthVal,
const char *valVal = "",
bool canEditVal = true)
{
std::cout << valVal << std::endl;
if(strlen(valVal) > 0)
{
}
else {
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Whenever there is strange behavior like this, memory getting screwed up is almost always the culprit. Never mix new with delete[] OR new[] with delete. The latter is slightly worse than the former but they are both bad news. delete[] should only be used in conjunction with new[]. Mixing these allocation/deallocation notations will result in undefined behavior. Since you are never using new[], replace all of your delete[] calls with delete. It is also good practice and good manners to set your pointers to NULL after you delete them. It is highly unlikely that you will be the only one debugging this code and it would be extremely annoying to debug your pointers thinking that there is valid memory there, when in fact there isn't.
Mixing these notations inevitably lead to exploits and memory leaks.
There is a problem here:
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
val is uninitialized when the constructor is called, and you are deleting it.

assignment operator overloading problem

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.
}