#include <iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *sptr;
public:
String()
{
}
String(char str[])
{
sptr = new char[strlen(str) + 1];
strcpy( sptr, str );
}
String(const String& source)
{
sptr = new char[strlen(source.sptr) + 1];
strcpy( sptr, source.sptr);
}
~String()
{
delete sptr;
}
String operator=( const String& other )
{
if(&other!=NULL)
{
String tmp( other );
sptr = new char[strlen(tmp.sptr) + 1];
strcpy( sptr, tmp.sptr);
}
return *this;
}
void display()
{
for( char const* p = sptr; *p != '\0'; ++ p) {
std::cout << *p;
}
cout<<endl;
}
};
int main()
{
String a1;
String a2("Hello ");
String a3(a2);
a2.display();
a3.display();
//a2.change("Java ");
a2.display();
a3.display();
}
The Output pf the program is
Hello Hello Hello Hello.
But I am undone to do the following changes in my code... i.e.. In Main () I created the following objects
String a1;
String a2("Hello ");
String a3(a2);
a2.display();
a3.display();
//a2.change("Java ");
a2.display();
a3.display();
I want to change the the object a2(commented) with Java and desired the output as
Hello Hello Java Hello
through deep copy of the Data members through this pointers.
How can I change the Hello string to java
Your String class cannot be used to build a program upon, due to the faults within the String class. You must fix those errors first before even thinking about using String as a string class.
Therefore this answer addresses how to fix String first -- from there, you should now be able to write the program knowing you're not working on a faulty foundation.
Error 1: Default construction
You failed to initialize sptr when a String is default constructed. Thus when ~String() is executed, the call to delete will attempt to delete an uninitialized pointer.
Simple 1 line main functions such as this one:
int main()
{
String s;
} // <-- destructor of s may crash
will show undefined behavior and may crash, as shown by this example.
To fix this:
class String
{
public:
String() : sptr(nullptr) {}
//...
};
Now when delete [] is issued on sptr, no harm will occur since it is perfectly valid to delete [] a nullptr (it becomes basically a no-op).
Error 2: Faulty assignment operator
Your String::operator= fails to deallocate the previous memory that was allocated. In addition, the check for NULL is incorrect, since &other will always be true.
I am assuming this is the check for self-assignment, but it is not written correctly. The check for self-assignment is done by comparing the address of the current object (this) with the address of the object being passed in:
if (this != &other)
Once you have that, then you could have written the rest of the function this way:
if(this != &other)
{
String tmp( other );
std::swap(tmp.sptr, sptr);
}
return *this;
All this function does is copy other to a temporary String called tmp, swap out the sptr with the tmp.sptr and let tmp die off with the old data. This is called the copy / swap idiom, where you're just swapping out the contents of the current object with the contents of the temporary object, and letting the temporary object die off with the old contents.
Note that the check for self-assignment isn't necessary when using copy / swap, but there is no harm done in making the check (could even be a small optimization done when the check for self-assignment is present).
Edit: The other issue is that operator= should return a reference to the current object, not a brand new String object. The signature should be:
String& operator=( const String& other ) // <-- note the return type is String&
{
//... code
//...
return *this;
}
Error 3: Wrong form of delete
Since you allocated using new [], you should be using delete [], not just delete in the destructor:
~String()
{
delete [] sptr;
}
Given all of these changes, the following code no longer has issues, as shown by the Live Example here.
Now that you have a working String class, then you can build your application from there.
As to your program, you can easily change your string by using the assignment operator. There is no need for a change() function:
String a2("Hello ");
a2.display();
a2 = "Java ";
a2.display();
Since String::operator=(const String&) is no longer faulty with the changes above, assignment can now be done without issues.
Related
I am a new user here and this is my first question, so please don't judge me hard. I cheked many similar questiontions like this not only on this site, also on others too. But I didn't find the answer.
The problem is to create copy constructor with pointers. For those people who may ask: why do you need pointers?: I have a class stmt, which contains vector<stmt>, it will allow me to construct tree-like data structure. Then I will perform some functions to change values of stmt's parameters. Without pointers they won't change
It compiles, but then gives Exception Unhandled (Runtime error)
My first attemp looks like this:
struct stmt
{
string lexeme;
int *size;
int *lay;
bool *nl;
vector<stmt>* follow;
stmt(string l)
{
lexeme = l;
size = new int;
*size = l.size()+2;
lay = new int;
*lay = 0;
nl = new bool;
*nl = false;
follow = new vector<stmt>;
}
stmt(const stmt &s)
{
lexeme = s.lexeme;
size = new int; //Crashes here : Unhandled exception:std::length_error at memory location ...
*size = *s.size;
lay = new int;
nl = new bool;
follow = new vector<stmt>;
follow = s.follow;
}
};
Second time I tried also this:
stmt(const stmt &s)
:lexeme.(s.lexeme), size (s.size), ......and so on
{}
Unfortunately, this also doesn't help.
So, this is my third attemp but didn't help
IMPORTANT: I noticed that it happens when I am trying to create and emplace_back new stmt element in a vector of another stmt using functions which return type is stmt.
Here is a code which represents the key problem:
stmt Program("Program");
stmt ParseRelop(string p);
void EMP(stmt s)
{
s.follow->push_back(ParseRelop("token"));
}
stmt ParseRelop(string p)
{
stmt s(p);
return s;
}
int main()
{
EMP(Program);
cout<<Program.follow->at(0).lexeme;
}
Like this
stmt(const stmt &s)
{
...
follow = new vector<stmt>(*s.follow);
}
This version allocates a new vector by copying the vector from the copied object (*s.follow is the vector in the copied object).
Your version allocated a new pointer but then overwrote it with a copy of the pointer from the copied object.
You want to copy the vector, not the pointer that's pointing at the vector.
You should use the same technique for your other pointers
size = new int(*s.size);
lay = new int(*s.lay);
etc.
Let's start by making your stmt class copy correct. Once you do that, then the issues will come from other parts of your code that do not involve stmt leaking memory, or accessing invalid memory:
#include <vector>
#include <string>
#include <algorithm>
struct stmt
{
std::string lexeme;
int *size;
int *lay;
bool *nl;
std::vector<stmt>* follow;
// Constructor. Note the usage of the member initialization list
stmt(std::string l) : lexeme(l), size(new int), lay(new int(0)), nl(new bool(false)),
follow(new std::vector<stmt>())
{
*size = l.size() + 2;
}
// Copy constructor. Note that we use the member initialization list,
// and copy all the members from s to the current object, not partial copies
stmt(const stmt &s) : lexeme(s.lexeme), size(new int(*s.size)), nl(new bool(*s.nl)),
lay(new int(*s.lay)), follow(new std::vector<stmt>(*s.follow))
{}
// The assignment operator. Note that we use copy / swap to swap out
// all the members, and that a check for self-assignment is done.
stmt& operator=(const stmt &s)
{
if (this != &s)
{
stmt temp(s);
std::swap(lexeme, temp.lexeme);
std::swap(size, temp.size);
std::swap(lay, temp.lay);
std::swap(nl, temp.nl);
std::swap(follow, temp.follow);
}
return *this;
}
// The destructor destroys all the memory
~stmt()
{
delete size;
delete lay;
delete nl;
delete follow;
}
};
This class now follows the rule of 3 properly, and should be safely copyable. I put in the comments what each function does, and you should research on why the class should work correctly now.
I didn't want to go too much in depth, because in reality you wouldn't have pointers to int, bool or even vector<stmt>.
References:
Rule of 3
Copy / Swap Idiom
I have a class that contains the operator + () to add 2 objects. The class contains an attribute char * variable str that points to an allocated char array from new char[] that contains a C style text string. I want the operator +() to concatenate two char arrays into a third char array allocated as a new buffer sized to contain the concatenated string as the result of the operator.
I am using overloaded constructor and initializing list and I initialize the variable to a default value. The destructor I've added to the class frees the memory allocated for the object text buffer in the char *str variable.
Everything works perfectly fine WITHOUT the class destructor, but as soon as I add the destructor, the program prints weird characters. I am also seeing a memory leak.
Here is the code and note that the class destructor is included here!
#include <iostream>
#include <cstring>
#include<cstring>
class Mystring {
private:
char *str;
public:
Mystring():
str{nullptr} {
str = new char[1];
*str = '\0';
}
Mystring(const char *s):
str {nullptr} {
if (s==nullptr) {
str = new char[1];
*str = '\0';
} else {
str = new char[std::strlen(s)+1];
std::strcpy(str, s);
}
}
Mystring(const Mystring &source):
str{nullptr} {
str = new char[std::strlen(source.str)+ 1];
std::strcpy(str, source.str);
std::cout << "Copy constructor used" << std::endl;
}
~Mystring(){
delete [] str;
}
void print() const{
std::cout << str << " : " << get_length() << std::endl;
}
int get_length() const{
return std::strlen(str);
}
const char *get_str() const{
return str;
}
Mystring&operator+(const Mystring &dstr)const;
};
// Concatenation
Mystring &Mystring::operator+(const Mystring &dstr)const {
char *buff = new char[std::strlen(str) + std::strlen(dstr.str) + 1];
std::strcpy(buff, str);
std::strcat(buff, dstr.str);
Mystring temp {buff};
delete [] buff;
return temp;
}
int main() {
Mystring m{"Kevin "};
Mystring m2{"is a stooge."};
Mystring m3;
m3=m+m2;
m3.print();
return 0;
}
Mystring &Mystring::operator+(const Mystring &dstr)const
This states that it's going to return a reference to a MyString Your temp however is local. This results in a far more serious issue than just a memory leak.
Once you're returning by value; you'll find that it might end up wanting to use the copy or move constructor; which leads to (as said in the comments) the rule of 3 (or rule of 5).
This
Mystring &Mystring::operator+(const Mystring &dstr)const {
char *buff = new char[std::strlen(str) + std::strlen(dstr.str) + 1];
std::strcpy(buff, str);
std::strcat(buff, dstr.str);
Mystring temp {buff};
delete [] buff;
return temp;
}
contains a bug. You create a temp object and return it by reference (Mystring&). That reference turns into a dangling one as soon as the function returns and using it is undefined behaviour. The fact that the code appears to work with or without the destructors means nothing. Undefined behaviour is undefined and you cannot expect anything from the code that invokes it.
You may simply change the return type of Mystring::operator+ to return by value (simply Mystring).
Although, you would encounter another problem later on.
m3=m+m2;
performs a copy assignment, which uses T& operator = (const T&). You did not provide one, so the compiler generated one.
Why is that bad? Because automatically generated special member functions perform shallow copies (if they are used for copying), which means that if you have, for example, pointer member variable, the pointer will be copied. Just the pointer. Not the data it points to. Just the pointer. You end up with two pointers pointing to the same memory and once a correctly defined destructor tries to free those resources, you encounter a double delete[] error, which, again, is undefined behaviour for non-nullptr pointers.
You may want to look forward into the rule of three, and then probably into the rule of five.
I'm trying out a C# style string implementation in C++.
I have created an object and a pointer for class String, and assigned the object to the pointer. When i try to modify the object via the pointer instead of modifying the existing object i want to create a new object and make the pointer point it.
So i have overloaded the "=" operator, and creating a new object in the operator overloaded method. In order to reflect the change i need to use ss=*ss = "name";
Any suggestion to improve this code.
Below is my sample code
class String
{
char *str;
public:
String(char* str_in)
{
str = new char[strlen(str_in)];
strcpy(str, str_in);
}
String* operator=(char* s)
{
return new String(s);
}
};
int main()
{
String s="sample";
String *ss;
ss = &s;
ss=*ss = "name";
return 0;
}
I also tried to modify the this pointer, but not working as expected
String *ptr;
ptr = const_cast<String*>(this);
ptr = new String(s);
I would recommend some changes like this:
#include <string.h>
class String
{
char *str;
public:
String(const char* str_in)
{
str = new char[strlen(str_in)];
strcpy(str, str_in);
}
~String()
{
delete [] str;
}
String& operator=(const char* s)
{
char* tmp = new char[strlen(s)];
strcpy(tmp, s);
delete [] str;
str = tmp;
return *this;
}
};
int main()
{
String s("sample");
String *ss;
ss = &s;
ss = new String("name");
delete ss;
return 0;
}
First of all, you need an appropriate destructor or you are going to have a memory leak when String gets destroyed. Deleting the char* fixes this (since it is an array, we use the array delete).
Secondly, in C++, we almost always return a reference for operator= (not a pointer). So this revised operator= function is probably better - it deletes the old string, allocates memory for the new string, copies the new string, and returns *this.
Third, you can use const char* instead of char* for the constructor and assignment operator since you are not editing it.
In main(), I also created a new object for the pointer to point to since you requested that in the original post (and then it is deleted afterwards to avoid a memory leak).
Let me know if you have any questions with those changes.
With regard to this line:
The s[9] that goes through void setChar(size_t index, char c), but what is *this (inside the if, in the line: ``)?
Is it s1 or nullptr? Then what is _str (in the same line: *this = _str;), is it s1?
class MyString {
size_t _size = 0;
char* _str = nullptr;
int* _pRefCount = nullptr;
}
operator char() const {
return ((const MyString&)_s)[_index];
}
};
void detach() {
if(_pRefCount && --*_pRefCount == 0) {
delete []_str;
delete _pRefCount;
}
}
void attach(const MyString& str) {
_size = str._size;
_str = str._str;
_pRefCount = str._pRefCount;
++*_pRefCount;
As always *this is the object the function is executed for. If you are inside s1[4] the *this would be s1.
Using *this = _str; is just an involved way of calling one of the operator= overloads for MyString. It has the effect of "unsharing" by assigning a new copy of _str to itself.
Also, it has been known for quite some time that reference counted strings is not an optimization. For example in the output of s1[0] the code will create an unnecessary CharProxy just in case someone would assign a value to it. You also get a separate memory allocation for the refcount of each string you create, even if you never intend to share it
Please refer below program before answering question. Explained the code in comments.
So my question here is in assignment operator overloading how to handle the case where new() failed to allocate memory.
For example Obj1 is holding string "GeeksQuiz". Assigning Obj2 to Obj1. During assigning (in assignment operator overload function) first we free Obj1 and then recreate Obj1 with Obj2 values. So in the case where new fails to allocate memory how to retain old Obj1 values? Since we freed Obj1 values in starting of function.
All that I want is to have the old values for Obj1 when the assigning operation fails.
Please help me in this. I want perfect code, without any memory leaks covering all scenarios. Thanks in Advance
#include<iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *string_data;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] string_data; }// destructor
void print() { cout << string_data << endl; } // Function to print string
String& operator = (const String &); // assignment operator overload
};
String::String(const char *str) // Constructor
{
size = strlen(str);
string_data = new char[size+1];
if (string_data != NULL)
strcpy(string_data, str);
else
cout<<"compiler failed to allocate new memory";
}
String& String::operator = (const String &str) // assignment operator overload
{
if(this != &str)
{
delete [] string_data; // Deleting old data and assigning new data below
size = str.size;
string_data = new char[size+1];
if(string_data != NULL) // This condition is for cheking new memory is success
strcpy(string_data, str.string_data);
else
cout<<"compiler failed to allocate new memory"; // My quetsion comes in this scenario...
}
return *this;
}
int main()
{
String Obj1("GeeksQuiz");
String Obj2("stackoverflow");
Obj1.print(); // Printing Before assigment
Obj2.print();
Obj1 = Obj2; // Assignment is done.
Obj1.print(); // Printing After assigment
Obj2.print();
return 0;
}
First of all, implementing a robust string is difficult, unless you want to do it for learning purposes always use std::string.
Then take into account that operator new always returns non-null pointers (unless you are also implementing a non standard custom new operator), instead it throws a std::bad_alloc exception if it fails to allocate the data. If you want to handle the allocation fail case you need to add a try-catch block
char *data = NULL;
try {
data = new char[str.size + 1];
} catch (std::bad_alloc &e) {
std::cout << "Allocation failed: " << e.what() << std::endl;
throw; // <- You probably want to rethrow the exception.
}
strcpy(data, str.string_data);
delete [] string_data;
string_data = data;
size = str.size;
The important part is to leave your class in a consistent state when the exception is thrown, that's why you must first allocate the new data and then if it succeeds, delete the old data. However bad_alloc exceptions are rarely handled at class level, commonly you let the exception be thrown (that's why I rethrow in the code sample) and let the client code handle that.
If you really want your code to be exception proof I would advice the use of smart pointers, and as already said, in this case use std::string.
Temporary or dummy variables.
Allocate new memory, assign pointer to a temporary variable. If it succeeds then free the old memory and reassign that pointer variable.
Pseudo-ish code:
char *temp = new char[new_size];
std::copy(new_data, new_data + new_size, temp);
delete [] old_data;
old_data = temp;
old_size = new_size;
1st allocate memory in a temporary variable, if it's successful then only delete old value.