I am creating a class that works similar to the string class. I am in a starting stage doing some experiments.
Assume all header files are added
class String
{
public:
int len;
char *str = NULL;
// Default constructor
String()
{
len = 0;
str = new char[1];
str[0] = '\0';
printf("New memory: %p\n", str);
}
// Copy constructor
String(const char *ch)
{
len = strlen(ch);
if (str != NULL)
{
printf("Old memory: %p\n", str);
delete str;
printf("Deleted...\n");
}
str = new char[len + 1];
printf("New memory: %p\n", str);
for (int i = 0; i <= len; i++)
str[i] = ch[i];
}
};
int main()
{
String msg;
std::cout << msg.len << std::endl;
std::cout << msg.str << std::endl;
msg = "Hello";
std::cout << msg.len << std::endl;
std::cout << msg.str << std::endl;
}
When I create a String object 1 byte of memory is allocated to store string and initialises with \0 on line String msg;
When copy constructor is called on line msg = "Hello"; it first checks for any previous allocation of memory and to avoid memory wastage it will delete that and recreates a new memory block.
The issue is when copy constructor is called line if (str != NULL) is not getting executed. It should execute because str is allocated a byte of memory earlier in default constructor and it is not NULL.
What may be the thing that I missed out?
Your code does this:
Default-construct a String "A"
Convert-construct a temporary String "B" from the expression "Hello"
Copy-assign "B" to "A"
The assignment does not invoke the constructor on "A". You only get one construction per object.
Unless you specify otherwise (with delegating constructors), your default constructor is not invoked before the "copy constructor" (or a conversion constructor like yours) is run.
So this:
It should execute because str is allocated a byte of memory earlier in default constructor and it is not NULL.
is wrong.
String(const char *ch) needs to allocate.
Also, because you're performing assignment, you're going to need to implement a copy assignment operator to make that safe.
Related
This question already has answers here:
About the usage of new and delete, and Stroustrup's advice
(3 answers)
Closed 1 year ago.
I'm solving the following exercise (17.4) from Stroustrup's PPP book:
Write a function char* strdup(const char* ) that copies a C-style string into memory it allocates on the free store. Don't use any standard library function.
Here's my implementation, which compiles just fine. I have a question about an error message that I found when I run the function.
#include <iostream>
#include <string>
char* strdup(const char* s) {
if (s==0) return 0;
// get number of char in s
int n = 0;
while (s[n] != 0)
++n;
// allocate memory with room for terminating 0
char* pc = new char[n+1];
// copy string
for (int i = 0; s[i]; ++i)
pc[i] = s[i];
pc[n] = 0; // zero at the end: it's a C-style string
delete[] s;
return pc;
}
int main()
try {
std::string str;
char* cstr;
while (std::cin>>str && str!="quit") {
cstr = strdup(&str[0]);
std::cout << cstr << "\n";
delete[] cstr;
}
}
catch (std::exception& e) {
std::cerr << "exception: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "exception\n";
}
It compiles, but when I run it and I write the first character, I have a pointer being freed was not allocatederror. If I remove delete[] s, then I have no memory leak and it runs just fine. But why is (apparently) correct to do not delete[]the s? Is it because it has not been allocated with new?
A std::string does manage the memory it uses to store the string. In main you do
std::string str;
and
cstr = strdup(&str[0]);
but your strdup calls delete[] s; on the parameter.
This is what you already know. Now consider that the destructor of std::string does already clean up the memory when it goes out of scope. The buffer used by the std::string cannot be deleted twice. You shall not call delete on &str[0]. You only need to delete objects that you created via new.
Also there is small string optimization. In this case &str[0] does not point to a heap allocated buffer which you could delete.
PS: You are using 0 when you should rather use nullptr for pointers and '\0' for the null terminator.
class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
//String(const String&); // copy constructor --> i get double free error while running without this
void print() { cout << s << endl; } // Function to print string
void change(const char *); // Function to change
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
void String::change(const char *str)
{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
/*String::String(const String& old_str)
{
size = old_str.size;
s = new char[size+1];
strcpy(s, old_str.s);
}*/
int main()
{
String str1("Hello");
String str2 = str1;
str1.print(); // printed
str2.print();
str2.change("Helloworld");
str1.print(); // not printed
str2.print();// printed
return 0;
}
http://ideone.com/xJtoTf
I am getting a double free error and the print for str1 is not being printed the second time (see comments in the code above)...
Is it because when i am not using copy constructor here, instead of default copy constructor being invoked assignment operator is used and both str2 and str1 are pointing to same location?
String str2 = str1; calls the copy constructor, not the assignment operator.
As you don't provide the copy constructor, the compiler will supply one automatically. All that will do is copy the member data.
So both str2 and str1 will share the same character buffer. Boom!
I am coding this in C++. My current issue at hand is that I have to trim the whitespace from the beginning of the character array. I am not allowed to use any string functions. My idea is to count the number of whitespaces at the beginning, allocate memory based on how much less memory I would need in a character array if I didn't have those whitespaces, do so, and then copy over the new string and deallocate the original string.
My issue is that I can't seem to deallocate that string without Visual Studio hitting a break point for me. I can get it working with the code I have below, (not deallocating the roginal strig at all) d=but wouldn't that cause a memory leak?
Thanks for your help in advance.
#include <iostream>
using namespace std;
class SmartString{
private:
char* str;
public:
SmartString ( )
{
str = NULL;
}
SmartString (char *str){
int length = 0;
int copy_index = 0;
while(str[length] != '\0')
{
length++;
}
length++;
char * copy;
copy = (char*)malloc(sizeof(char) * length);
copy = new char[length];
while(copy_index < length)
{
copy[copy_index] = str[copy_index];
cout << str[copy_index];
copy_index++;
}
this -> str = copy;
}
~ SmartString()
{
if(str != NULL)
{
delete str;
free(str);
}
}
void ShowString()
{
cout << "[" << str << "]";
}
int Size()
{
if(str == NULL)
return 0;
else
{
int i = 0;
while(str[i] != '\0')
{
i++;
}
i++;
return i;
}
}
**void Trim()
{
int counter = 0;
while (str[counter] == ' ' && counter < Size())
{
counter++;
}
int new_length = Size() - (counter + 1);
char * temp;
temp = (char*) malloc(sizeof(char) * new_length);
temp = new char[new_length];
int counter_2 = 0;
while(counter_2 < Size())
{
temp[counter_2] = str[counter_2 + counter];
counter_2++;
}
str = temp;
}**
};
int main()
{
char *str;
str = " Hello";
SmartString * s = new SmartString(str);
str = "Change";
(*s).Trim();
(*s).ShowString();
system("Pause");
}
You have not used 'delete' in your main function to deallocate your 's' pointer variable, So that the destructor method of your 'SmartString' class never called. In your second constrcutor method, you I've allocated the 'copy' variable twice where it's not need And also you have some mistake in your 'Trim' method.
In your destructor method, You should remove the free(str); statement cause the delete str; statement will deallocate the 'str'. So there is no need to deallocate twice.
malloc - Allocates the requested memory and returns a pointer to it.
new X; - Do the same thing but also calls constructor method if X is a class or struct after allocating.
new X[] - Allocates dynamic array with the requested memory and returns a pointer to it.
free - Deallocates the memory previously allocated.
delete - Do the same thing but also calls destructor method if X is a class or struct after deallocating.
delete[] - Deallocates the memory previously allocated dynamic array.
new and delete is the standard memory allocation and deallocation implement of C++ language where malloc and free is the standard memory allocation and deallocation function of C language.
Here I've rewritten your 'Trim' method:
void Trim()
{
int counter = 0;
while (str[counter] == ' ' && counter < Size())
{
counter++;
}
int new_length = Size() - (counter + 1);
char * temp;
// There is no need to allocate twice
//temp = (char*) malloc(sizeof(char) * new_length);
temp = new char[new_length+1];
int counter_2 = 0;
while(counter_2 < //Size() ( Here is your big mistake. You should not use 'Size()' here )
new_length
)
{
temp[counter_2] = str[counter_2 + counter];
counter_2++;
}
temp[counter_2] = 0;
str = temp;
}
And for deallocating, you have to use the 'delete' like this:
int main()
{
char *str;
str = " Hello";
SmartString * s = new SmartString(str);
str = "Change";
(*s).Trim();
(*s).ShowString();
// use delete to deallocate a pointer
delete s;
system("pause");
}
I see three reasonable approaches to this.
One would be to modify the existing string in-place. Find the position of the first non-space character, then copy from there to the end of the string to positions starting from the first element of the string. This can't be applied to a string literal (or you'll get undefined behavior).
The second would be to allocate a new buffer and copy the data you want to keep into that buffer. In this case, you probably do not want to try to modify the original (and, especially, you don't want to try to free its data).
The third would be to (basically) re-implement a class about like std::string, that always allocates a buffer in a specific way, so it "knows" how to manipulate that buffer safely. In this case, you could/would have a constructor to create an object from a string literal, so by the time your function was invoked, it would only (even attempt to) manipulate such objects and could never accidentally try to manipulate/modify something like a string literal.
So I'm studying this book, and I came across an exercise which (briefly) wants me to remove all white spaces of a char-array by using a function: void removeSpaces(char* s)
[iostream, cstring are included and SIZE is defined]
This is main():
int main() {
char a[SIZE] = "a bb ccc d";
cout << a << endl; // a bb ccc d
removeSpaces(a);
cout << a << endl; // a bb ccc d instead of abbcccd
}
This is removeSpaces():
void removeSpace(char* s) {
int size = strlen(s);
char* cpy = s; // an alias to iterate through s without moving s
char* temp = new char[size]; // this one produces the desired string
s = temp; // s points to the beginning of the desired string
while(*cpy) {
if(*cpy == ' ')
cpy++;
else
*temp++ = *cpy++;
}
cout << s << endl; // This prints out the desired result: abbcccd
}
(My choice of names isn't ideal, but nevermind that now.) So my function basically does what I want it to do, except that the result has no effect outside of the function's scope. How can I accomplish that? What am I missing, what am I doing wrong?
Since you pass the pointer by value, you are surely changing the array in place. Obviously, you'd remove spaces like this:
void removeSpaces(char* s) {
*std::remove(s, s + strlen(s), ' ') = 0;
}
Your function shouldn't even do any string copying. It should just do an in-place replacement:
void removeSpace(char* s)
{
for (char* s2 = s; *s2; ++s2) {
if (*s2 != ' ')
*s++ = *s2;
}
*s = 0;
}
Even terser:
void removeSpace(char* s)
{
char* s2 = s;
do {
if (*s2 != ' ')
*s++ = *s2;
} while (*s2++);
}
You change the value of s but that is a local variable and thus has no affect outside the function:
void removeSpace(char* s)
You could change this to:
void removeSpace(char*& s)
This makes a reference to the passed value. Thus changing s will change the original. Unfortunately that does not work for you because of the way you call removeSpace() (as you pass an array).
char a[SIZE] = "a bb ccc d";
removeSpaces(a);
You could change your code too:
char buffer[SIZE] = "a bb ccc d";
char* a = buffer;
removeSpaces(a);
Now the modifications suggested above would work correctly. But you are leaking memory (you dynamically allocate memory in removeSpace() that is never released. The best way to resolve that is to use modern C++ techniques rather than write C and and compile it with the C++ compiler.
Couple of solutions:
Modify the array in place (no dynamic allocation)
Use proper containers (std::string or std::vector)
Use the standard algorithms.
The call to removeSpaces has no affect because you are creating a temporary buffer in the function and copying the string into that while applying the transformation. You can fix this by removing the temporary buffer and just modifying the string in place.
void removeSpaces(char* s)
{
char* cpy = s; // an alias to iterate through s without moving s
char* temp = s;
while (*cpy)
{
if (*cpy != ' ')
*temp++ = *cpy;
cpy++;
}
*temp = 0;
cout << s << endl; // This prints out the desired result: abbcccd
}
Just add one last line to your function:-
strcpy(s, temp);
and remove unnecessary ones:-
s = temp;
Your function receives a copy of the pointer a. Changing the copy within the function does not change the caller's original a pointer.
#include<iostream>
using namespace std;
class MyString{
private:
char *str=new char[10];
public:
MyString(){*str='\0';} //default constructor
MyString(char *s){ //parameterized constructor
str=s;
}
private:
int length(char* s){
int i=0;
while(s[i]!='\0')
i++;
return i;
}
char* delchar(char* s,int count,int start){
int i,j=0;
char *temp= new char[10];
for(i=start;i<start+count;i++){
s[i]=' ';
}
for(i=0;i<length(s);i++){
if(s[i]!=' ')
temp[j++]=s[i];
}
s=temp;
return s;
}
public:
MyString operator-(MyString s){
int i=0,j=0,count=0,start=-1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str=str;
while(temp.str[i]!='\0'){
j=0;
start++;
while(s.str[j]!='\0'){
if(temp.str[i]==s.str[j]){
count++;
i++;
j++;
if(count==length(s.str)){//checks if the count
temp.str=delchar(temp.str,count,start);
i=i-count;
start=i-1;
count=0;
}
}
else{
i++;
count=0;
break;
}
}
}
return temp;
}
~MyString(){
delete str;
}
friend ostream &operator<<(ostream &stream,MyString& s){
stream<<s.str<<endl;
return stream;
}
};
int main(){
char *p= new char[20];
char *q= new char[10];
cin>>p;
cin>>q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3=s1-s2;
cout<<s3;
delete p;
delete q;
return 0;
}
The above code overloads the - operator .It tries to subtract the substring from the main string for example input1:joshmathews input2:josh output:mathews. I am trying to store the output in a new MyString object s3. When I use a destructor as shown above,outputting s3
returns null. But when I don't use a destructor I get the expected output.Can anyone help?
The primary issue is that Operater- returns a local object which is copied by a default copy constructor -- that default copy constructor points s3 to the exact same memory/buffer of the temp MyString, and when that temp is destructed, it wipes out the memory s3 is using.
That is referred to as a dangling pointer. You can read more here: http://en.wikipedia.org/wiki/Dangling_pointer#Cause_of_wild_pointers
The code below is the changes I made to get your program executing and returning a valid result, while completing without error. To be clear though, there are issues with this altered version, runtime error or no runtime error, but this will help illuminate some important points.
When you define a type that allocates memory, you've really started to do somethings that don't come free. My altered version below completely got rid of the destructor, so actually it leaks memory up until the program ends. The destructor was invalid though, so removing it allowed the program the finish. But this would be one of those things that in general you shouldn't just accept.
I added a copy constructor and a copy assignment operator:
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
Notice the strcpy_s in both of these. What is doing is copy the character string from the argument instead of just trying to point at the exact same string at the exact same address as the argument. If the argument gets destructed in your version, it wipes out some memory, so we can just accept default copy constructor and such since by default they are shallow copies that point to the same guts. That's one of the burdens of allocating memory -- you need to take care of that in both your destructor's ~and~ some of your constructors. This is referred to as "The Rule of Three":
If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.
Here's a wikipedia link about the rule of three: http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
You needed a destructor, so that's the clue you need the others. The reason is as I was saying -- by default you get a copy constructor for free, but it just makes everything point to the same guts if there are points are resources, but then you can't delete one without affecting everybody else.
Also, in delchar I added this line towards the bottom:
temp[j] = '\0';
Characters strings must always end at a null, and you copied only actual letter characters into temp, so without a null character, a strcpy doesn't know where to end.
If you remember I yanked out your destructor, then those are the changes I made.
The destructor also has an issue, in that if you use new to create an array, like
char* c = new char[10];
then you need to delete it in a way that indicates in was new'd as an array, like so:
delete [] c;
The next thing you should look into is how your MyString construction happens. If you step through, you'll see that the str member get new'd -- that means it holds a valid address into a buffer you can use -- but then if the MyString(char *s) constructor is used, it literally just takes this new address of s, and makes str hold that address instead -- which means the old address pointing to the valid buffer is lost, and that buffer of new'd memory cannot be freed. So that's a leak. You could use the strcpy_s strategy from the constructors I added to copy the contexts of what is pointed to by the s in MyString(char *s) into the new'd buffer. That would make a huge positive difference in the code.
And don't bother about all the elitists out there -- many of them were born doing headstands, so they can't see to relate to newbies giving things an honest effort.
Here's the full altered code:
#include<iostream>
using namespace std;
class MyString{
private:
char *str = new char[10];
public:
MyString(){ *str = '\0'; } //default constructor
MyString(char *s){ //parameterized constructor
str = s;
}
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
private:
int length(char* s){
int i = 0;
while (s[i] != '\0')
i++;
return i;
}
char* delchar(char* s, int count, int start){
int i, j = 0;
char *temp = new char[10];
for (i = start; i<start + count; i++){
s[i] = ' ';
}
for (i = 0; i<length(s); i++){
if (s[i] != ' ')
temp[j++] = s[i];
}
temp[j] = '\0';
s = temp;
return s;
}
public:
MyString operator-(MyString s){
int i = 0, j = 0, count = 0, start = -1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str = str;
while (temp.str[i] != '\0'){
j = 0;
start++;
while (s.str[j] != '\0'){
if (temp.str[i] == s.str[j]){
count++;
i++;
j++;
if (count == length(s.str)){//checks if the count
temp.str = delchar(temp.str, count, start);
i = i - count;
start = i - 1;
count = 0;
}
}
else{
i++;
count = 0;
break;
}
}
}
return temp;
}
~MyString(){
//delete str;
}
friend ostream &operator<<(ostream &stream, MyString& s){
stream << s.str << endl;
return stream;
}
};
int main(){
char *p = new char[20];
char *q = new char[10];
cin >> p;
cin >> q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3 = s1 - s2;
cout << s3;
delete p;
delete q;
return 0;
}