push_back struct into vector - c++

//prototype
void Split(char c, vector <MyString> &outputVector) const
//partial code inside split function
// create new MyString object to push into output vector
MyString substr;
substr.mString = newString;
substr.mLength = size;
// push new item
outputVector.push_back(substr);
After I step over the outputVector.push_back() line, the mString data member is not retained.
//I have two constructors
MyString()
{
mString = NULL;
mLength = 0;
}
/*************************************************
* MyList copy constructor
* creates a deep copy of a MyString item
************************************************/
MyString(const MyString &copy)
{
mString = new char[copy.mLength];
int i;
for(; i < copy.mLength; i++)
{ mString[i] = copy.mString[i]; }
mString[i] = '\0';
mLength = copy.mLength;
}

You are using an uninitialized variable which is undefined behavior
int i;
for(; i < copy.mLength; i++)
Here we have no idea what i is so anything can be going on but most likely i is larger than copy.mLength so we never enter the for loop. In order to get correct behavior set i to 0 like
int i = 0;
You have another issue with
mString[i] = '\0';
By the time we reach that line i == copy.mLength but the array only has the size of copy.mLength so we are one past the end since arrays are 0 index based. Most likely you need to change your allocation to
mString = new char[copy.mLength + 1];
to give you space for the null terminator.

http://www.cplusplus.com/reference/vector/vector/push_back/
push_back copies value to vector. Does MyString class have properly defined copy constructor, that copies mString member? I would guess this might be your problem.

I think there are 2 mistakes ,you have done
1.for(i; i < copy.mLength; i++)
{ mString[i] = copy.mString[i]; }
you have to mention ,where the loop will start .
2.mString = new char[copy.mLength + 1];
mString[i] = '\0';
i think ,you got the answer :)

Correct version of copy constructor
MyString(const MyString &copy)
{
mString = new char[copy.mLength + 1];
int i = 0;
for(; i < copy.mLength; i++)
{ mString[i] = copy.mString[i]; }
mString[i] = '\0';
mLength = copy.mLength;
}

Related

Attempting to troubleshoot copy constructor crash issue

I've been attempting to troubleshoot this code with no avail. It crashed just as I call the copy constructor. In other attempts at calling the copy constructor the output of the new object has been random characters.
I have a feeling that my lack of understanding of memory management carries over to overloading the = operator too.
Here is my copy constructor....
MyString::MyString(const MyString& obj)
{
delete [] str;
int temp=obj.len;
len = temp;
str = new char[len];
for (int i = 0; i < len; i++)
{
char temp=obj.str[i];
str[i] = temp;
}
}
Here is my overloaded = ....
MyString& MyString::operator=(const MyString& obj)
{
delete [] str;
int temp = obj.len;
len = temp;
str = new char[len];
for ( int i = 0; i<=len; i++)
{
char temp = obj.str[i];
str[i] = temp;
}
str[len] = '\0';
return *this;
}
in the main method I call the copy constructor like so...
MyString stt3(str2); //str2 already exists.
Can anyone suggest where I'm going wrong?
The line
delete [] str;
in the copy constructor is not right. str is not initialized before that line. Removing that line should help, if not remove all the problems.
Also, you probably need to allocate one more object than len and null terminate str.
str = new char[len+1]; // Need +1
...
str[len] '\0';
You also need to change the call to new char[] in the assignment operator function too. You need:
str = new char[len+1];

C++ - implementation of string class

I'm trying to implement string class. Here is what I have done:
#include <iostream>
#include <cstring>
using namespace std;
class MyString{
private:
char * content;
int length;
public:
MyString ();
MyString ( const char * );
~MyString ();
MyString ( const MyString & );
void print ( void );
void operator = ( const MyString );
};
MyString :: MyString () {
content = 0;
length = 0;
}
MyString :: MyString(const char *n) {
length = strlen (n);
content = new char [ length ];
for ( int i = 0 ; i < length ; i++ ){
content [i] = n [i];
}
content [length] = '\0';
}
MyString :: ~ MyString () {
delete [] content;
content = 0;
}
MyString :: MyString ( const MyString & x ) {
length = x.length;
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
content [length] = '\0';
}
void MyString :: print( void ) {
cout <<""<< content << endl;
}
void MyString :: operator = ( const MyString x ) {
length = x.length;
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
content [length] = '\0';
}
int main() {
MyString word1 ("stackoverflow");
MyString word2;
word2 = word1;
word1.print();
word2.print();
}
I compiled it and this is what I get:
stackoverflow
stackoverflow
Process returned 0 (0x0) execution time : 0.050 s
Press any key to continue.
Although it looks correct according to result above, I wonder is it really correct? I'm not so familiar with C-style strings so I'm concerned
for example about line:
content [length] = '\0';
Since C-style strings has null terminator at end, I wanted to terminate my array but is this correct way to do it?
I used dynamic memory allocation and I also wonder did I free resources properly?
Are there some memory leaks?
Thanks in advance.
EDIT1:
I also overloaded opeartor + (I want to join "MyStrings"), here is code:
MyString MyString :: operator + ( const MyString & x ){
MyString temp;
temp.length = x.length + length;
temp.content = new char [ temp.length + 1 ];
int i = 0, j = 0;
while ( i < temp.length ) {
if (i < length ) {
temp.content [i] = content [i];
}
else {
temp.content [i] = x.content [j];
j ++;
}
i ++;
}
temp.content [ temp.length ] = '\0';
return temp;
}
Here is main program:
int main()
{
MyString word1 ( "stack" );
MyString word2 ( "overflow" );
MyString word3 = word1 + word2;
word3.print();
word3 = word2 + word1;
word3.print();
}
And here is result:
stackoverflow
overflowstack
Process returned 0 (0x0) execution time : 0.040 s
Press any key to continue.
I hope there are no problems with this code :)
EDIT2:
Here is implementation of + operator using for loops, instead of while:
MyString MyString :: operator + (const MyString & x){
MyString temp;
temp.length = x.length + length;
temp.content = new char [temp.length+1];
for( int i = 0 ; i < length ; i++ ){
temp.content[i] = content[i];
}
for( int i = length , j = 0 ; i <temp.length ; i++, j++){
temp.content[i] = x.content[j];
}
content[temp.length] = '\0';
return temp;
}
It's maybe better now because there is no if :)
You are trying to assign content[length] a value, but you haven't allocated enough memory for content[length] to be accessed. If length == 10, then you can access content[0] thru content[9], but not content[10].
This can be fixed of course by removing the line content[length] = \0 from both constructors, or if you want to append \0 you should increase the value of length by 1.
Have you considered just using std::string internally?
Edit: #Thane Plummer was first to point this out in the comments!
A few other notes and suggestions because there are are least two more gotchas waiting to leap out and strike.
#include <iostream>
#include <cstring>
// using namespace std; DANGER! namespace std is huge. Including all of it can
// have tragic, unforeseen consequences. Just use what you need.
using std::cout;
using std::endl;
class MyString
{
private:
char * content;
int length;
// will use clone to reduce duplication in the copy constructor and operator =
void copy(const MyString & source);
public:
MyString();
// it is nice to name the variables in the definition. The header may be the
// only documentation the user gets.
MyString(const char * source);
~MyString();
MyString(const MyString &source);
void print(void);
// changed prototype to match the expected format operator= format
MyString & operator =(const MyString &source);
//OP asked about this in a previous question.
friend std::ostream & operator<<(std::ostream & out,
const MyString& towrite);
};
MyString::MyString()
{
// content = 0;
//destructor needs something to delete[]. If content isn't set to something,
//you'll get a big ka-BOOM! when the MyString is destroyed
content = new char[1];
content[0] = '\0'; //this has the advantage of printing an empty MyString
// without crashing
length = 0;
}
MyString::MyString(const char *source) // Variable names should describe their purpose
{
//DANGER: strlen will fail horribly if passed an unterminated string. At a
// loss at the moment for a good, safe solution. Look into strnlen, but
// even it can't help you here.
length = strlen(source);
content = new char[length + 1]; //Needed one extra character to fit the NULL
/* If we got this far without dying, strcpy is no threat which makes this redundant:
for (int i = 0; i < length; i++)
{
content[i] = n[i];
}
content[length] = '\0';
*/
strcpy(content, source);
}
MyString::~MyString()
{
delete[] content;
// content = 0; string is gone. No need to clear this
}
void MyString::copy(const MyString & source)
{
length = source.length;
content = new char[length + 1];
// assuming that the source MyString is correctly formatted this is once again safe.
strcpy(content, source.content);
}
MyString::MyString(const MyString & source)
{
copy(source); // use the copy method
}
void MyString::print(void)
{
cout << "" << content << endl;
}
MyString &MyString::operator =(const MyString &source)
{
copy(source); // use the copy method again.
return *this; // allows chaining operations
}
std::ostream & operator<<(std::ostream & out,
const MyString& towrite)
{
out << towrite.content;
return out;
}
int main()
{
MyString word0;
MyString word1("stackoverflow");
MyString word2;
word2 = word1;
MyString word3(word2); //testing copy constructor
word1.print();
word2.print();
cout << word3 << endl; //testing outstream overload
// test output of empty string
word0.print();
cout << word0 << endl;
}
Edit:
Realized after posting that since we know the lengths of the strings, there are significant performance gains from using memcpy(content, source.content, length+1); in place of strcpy.
There are two errors. One has already been stated by Thane Plummer in the comments and by Tas in the answers:
MyString :: MyString(const char *n) {
length = strlen(n);
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
content [length] = '\0';
}
if your string is the null terminated "abc\0", strlen will return 3 and not 4, so you'll only allocate 3 chars instead of 4 (edit: and to be complete, as previously stated, you indeed start to index from 0 and not 1, so content[length] will always overflow, even if you increase length)
The other error is less grave (and is actually legal but odd c++):
void operator = ( const MyString );
The copy assignment operator should take a const reference rather than a const value (otherwise you may uselessly call the copy constructor), and return a reference rather than void (so that you can chain some calls). The correct declaration is:
MyString& operator=(const MyString&);
The correct implementation is:
MyString& MyString::operator=(const MyString& x) {
length = x.length;
delete[] content;
content = new char [length];
for( int i = 0 ; i < length ; i++ ){
content [i] = x.content [i];
}
// actually not needed since x.content should already be null-terminated
// content[length - 1] = '\0';
return *this;
}

copy constructor failure... overloading, dynamic allocation

I'm working with dynamic strings, and there is an issue with copy constructor and overloading.
I'm using an operator+ to overload. the overloaded function is used to interpolate two char * strings.
here's just an excerpt of the code:
MyString::~MyString()
{
delete[] text; //char *text, as my private data
}
MyString::MyString(const MyString & obj)//copy constructor here
{
text = new char[obj.len];
for (int i = 0; i < obj.len; i++)
{
text[i] = obj.text[i];
}
len = obj.len;
}
MyString MyString::operator+(MyString & s)
{
MyString temp;
temp.len = len + s.len + 1;
int i;
for (i = 0; i < len; i++)
{
temp.text[i] = this->text[i];
}
for (int i = 0; i < s.len; i++)
{
temp.text[i + len] = s.text[i];
}
temp.text[len + s.len] = 0;
return temp;//PROBLEM HERE
}
and here's my problem:
the operator+ function calls both copy constructor and destructor before returning temp
and though there IS a copy constructor what is returned, is a deleted [] array.
what's the catch? Thanks
Lets say you have the following code
MyString s1; // Initialized to something
MyString s2; // Initialized to something else
MyString s3 = s1 + s2;
Then the expression s1 + s2 creates a temporary copy (the one returned by your operator+ function). This temporary copy is then passed to the copy-constructor to create s3 followed by the destruction of the temporary object.
That's the theory anyway, in reality the compiler will elide the copying.
Make these changes and try :
1. temp.text[len + s.len] = '\0'; //Null terminated
MyString**&** MyString::operator+(MyString & s)
{
.
.
.
}
//Return by reference, you will get the correct result.

Returning a local object results in garbage, while returning the same object temporary works fine

I'm currently implementing my own string class (just for training), but I'm experiencing some problems in my substr() method:
MyString MyString::substr(size_t position, size_t length)
{
if (checkBounds() || length == 0)
{
return MyString();
}
char* tmp = new char[length + 1];
memcpy(tmp, this->s + position, length);
tmp[length] = STRING_ESCAPE;
MyString result(tmp);
delete[] tmp;
tmp = nullptr;
return result;
}
When I call this method and print the return value (I'm printing the char array actuallay, not the object itself), I receive complete garbage, which is carried out as a bunch of squares.
But when I return a temporary object return MyString(tmp), everything works fine. Initially i suspected this issue is associated to the delete[] operation, but commenting it out shows no effect.
The MyString constructor which is called is the following:
MyString::MyString(const char* s)
{
size_t length = this->strlen(s);
this->sLength = length;
this->s = new char[length + 1];
for (size_t i = 0; i <= length; ++i)
{
this->s[i] = *s;
++s;
}
}
So where is my mistake? Thank you!
In the two places you create MyString, they are being created within the context of your substr function. The return statement has to make a copy of them. Your copy constructor is probably not doing what you would need it to do.
A simpler design is for your substr function to return a pointer to a MyString that you create with operator new.
You were indeed missing the copy constructor. Something like this would do the work:
MyString::MyString(const MyString& other) {
size_t length = strlen(other.s);
s = new char[length+1];
strcpy(s,other.s);
this->sLength = length;
}

Add characters to a character array c++

Can someone tell me what's wrong with the following?
I'm trying to add characters to a character array. name is a pointer to a character array in the MyString class.
void MyString::add_chars(char* c)
{
if(l < strlen(c)+strlen(name))
name = resize(name, l, sizeof(c));
int i,j;
for(i=0; i<strlen(c); i++) {
name[i+l-1] = c[i];
l++;
}
}
char* MyString::resize(char* vptr, int currentsize, int extra) {
char* temp = new char[currentsize + extra];
int i;
for (i = 0; i < currentsize; i++) {
temp[i] = vptr[i];
}
vptr = temp;
return vptr;
}
And in main:
MyString g ("and");
g.add_chars("baasdf");
cout << g.get_name() << "\n";
But get_name returns "andb". How can I fix my code?
Edit:
Updated code, still same result..
void StringList::add_chars(char* c)
{
char* my_new_string = resize(name, l, sizeof(char));
if( my_new_string != NULL )
{
delete [] name;
name = my_new_string;
}
int i,j;
for(i=0; i<strlen(c); i++) {
name[i+l-1] = c[i];
l++;
}
name[l-1] = '\0';
}
char* StringList::resize(char* vptr, int currentsize, int extra) {
char* temp = new char[currentsize + extra + 1];
int i;
for (i = 0; i < currentsize; i++) {
temp[i] = vptr[i];
}
vptr = temp;
return vptr;
}
This line is wrong:
name = resize(name, l, sizeof(c));
You should not take the sizeof(char*), which your c variable is, but you should do sizeof(char) or just 1.
Also, make sure that you do +1 on the size to take care of the zero termination char at the end of your string.
How can I fix my code?
Don't fix it. Throw it away and use vector<char> or just string.
But I insist, how can I fix my code!?
OK, OK, here is how...
Get a nice debugger, for example this one.
Step carefully through the code, constantly inspecting the variables and comparing them with what you expect them to be.
When you reach the call to resize, take note of sizeof(c) (assigned to extra parameter of resize). When you realize it is not what you expected, ask yourself: what is the purpose of sizeof, and you'll understand why.
BTW, you also have a memory leak and a very poor performance due all these strlens.
Firstly, am I right in assuming that this is a learning exercise for you in learning "how to create your own string class"? C++ has already got a built-in string type which you should always prefer for the most part.
the sizeof operator yields the size (in bytes) of its operand, which in this case is c whose type is char* - it looks like what you're actually after is the length of a null-terminated character array (a "C" string") - you're already using strlen, so I'd suggest you simply want to use that again. (taking a null-terminator into account too)
name = resize(name, l, strlen(c) + 1);
Note, that your code looks as if it suffers from memory leaks. You're assigning a new value to your name variable without clearing up whatever existed there first.
if(l < strlen(c)+strlen(name))
{
char* my_new_string = resize(name, l, strlen(c));
if( my_new_string != NULL )
{
delete [] name;
name = my_new_string;
}
}
EDIT: As other replies have pointed out, there's still plenty wrong with the code which could be resolved using C++'s string and vector.
Here's one possible way you could implement add_chars
void MyString::add_chars(char* c)
{
if( c != NULL && name != NULL )
{
size_t newlength = strlen(c) + strlen(name) + 1;
char* newstring = new char[newlength];
if( newstring != NULL )
{
size_t namelength = strlen(name);
size_t remaining = newlength - namelength;
strncpy( newstring, name, newlength );
strncpy( &newstring[namelength] , c, remaining );
delete [] name;
name = newstring;
}
}
}