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;
}
Related
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
char* str;
int size;
public:
MyString()
{
str = '\0';
size = 1;
}
MyString(const char* const s)
: str(new char[strlen(s)])
{
size = strlen(s) + 1;
strcpy(str, s);
}
MyString(const MyString& another)
: str(new char[another.size])
{
size = another.size;
for (int i = 0; i < size; i++)
str[i] = another.str[i];
}
~MyString()
{
delete[] str;
}
void set(const char* st)
{
size = (int)strlen(st) + 1;
str = new char[size];
for (int i = 0; i < size; i++)
str[i] = st[i];
}
bool isEqual(const MyString& other) const
{
if (size != other.size)
return false;
if (strcmp(str, other.str) == 0)
return true;
else
return false;
}
void print() const
{
for (unsigned int i = 0; i < size; i++)
cout << str[i];
cout << endl;
}
};
int main() {
MyString strs[] = {
MyString("C"),
MyString(),
MyString("Java")
};
strs[1].set("C++");
const int arraySize = sizeof(strs) / sizeof(MyString);
const MyString target("Java");
for (int i = 0; i < arraySize; i++) {
const MyString str(strs[i]); // copy constructor
if (str.isEqual(target)) {
cout << "[" << i << "]: ";
str.print();
break;
}
}
for (const MyString& str : strs) {
str.print();
}
}
In dev c++ it's working and there was no caution, but in visual studio 2019, catution like "heap corruption detected after normal block crt detected that the application" is occured. I don't what is problem.
When I debuged my code, it successfully worked befor the main fuction finished. When destructor worked, the caution was appeared.
Please help me :)
Your default constructor should not compile with recent GCC compilers, you can only assign NULL or nullptr to pointer
MyString() {
str = nullptr;
size = 0;
}
This constructor needs one extra 1 byte to allocate memory, then the buffer overflow can be avoided.
MyString(const char* const s) : str(new char[strlen(s) + 1]) {
size = strlen(s) + 1;
strncpy(str, s, size);
}
strncpy is a safer replacement for strcpy, I suggest you use this one.
You also need to add implementation for operator=, or the compiler-generated one will cause a memory leak once you use operator= to copy an object.
MyString& operator=(const MyString& another) {
if (&another == this) return *this;
delete[] str;
str = new char[another.size];
size = another.size;
// better to use strncpy here
for (int i = 0; i < size; i++) str[i] = another.str[i];
return *this;
}
A similar string implementation is discussed in this question, you may also reference the code there.
Demo
One problem is the initialization of the members in the default constructor:
str = '\0';
size = 1;
The character literal '\0 is equal to zero, which is equal to a null pointer. That assignment is equivalent to:
str = nullptr;
And yet you still say that the size of the string is 1, which is wrong since there is no string and the size should be zero.
Another (and the likely culprint of your problem) problem is that in your constructor MyString(const char* const s) you use strlen to get the length of a C-style null-terminated string for the allocation, but you forget that strlen doesn't count the null-terminator.
This means the allocation will be too small, and when you use strcpy to copy the string it will write the null-terminator out of bounds of your allocated memory.
Furthermore, you in other places count with the null-terminator, and actually set the size to include the null-terminator (even in the MyString(const char* const s) constructor). This goes against the normal C++ semantics of strings where the size doesn't include the terminator.
This could then lead to further problems, like in your print function where you actually print the null-terminator.
doing some exrecises for upcoming test. a bit stuck in this one.
"Write a program that asks the user for two strings and checks and prints a message if the second string is contained cyclic in the first string. The cyclic containment means that either the second string appears normally within the first string or the second string appears so that its prefix appears at the end of the first string and the continuation appears at the beginning of the first string".
You can assume that the strings contain only lowercase letters.
String functions are only allowed are : strlen, strcpy, strcmp, strcat
for example:
String A: itisaniceday
String B: sanic
Is a regular occurrence
String A: itisaniceday
String B: dayit
It's a cyclic occurence.
what I did so far:
#include <iostream>
#include <string.h>
using namespace std;
#define Max 128
int isCyclic(char* str1, char* str2);
int main()
{
char* str1 = new char[Max];
char* str2 = new char[Max];
cout << "Please enter two strings:" << endl;
cin >> str1 >> str2;
cout << isCyclic(str1, str2) << endl;
delete[] str1;
delete[] str2;
}
int isCyclic(char* str1, char* str2)
{
int s1 = strlen(str1);
int s2 = strlen(str2);
if (s1!=s2) // if string size is diffrent - they are not contained cyclic
{
return 0;
}
}
You will need two loops, first one over string 1 which is our starting point in string 1 for comparison and second one over string 2 which will be matched to string 1 in a cyclic way. If we reach the end of string 1 and still some characters are left in string 2 then cycle through string 1 starting from index 0.
#include <stdio.h>
#include <iostream>
#include <string.h>
// Should be avoided in general. Use scope resolution instead.
using namespace std;
char* isCyclic(char* s1, char* s2){
int s1_size = strlen(s1);
int s2_size = strlen(s2);
// s1 must contain s2
if(s2_size > s1_size)
return "No Occurence";
for(int i = 0; i < s1_size; i++){
int current = i;
// Boolean to track if we are currently cycling through s1
bool inCycle = false;
int j = 0;
for(; j < s2_size; j++, current++){
// character wise comparision
if(s2[j] != s1[current])
break;
if(! inCycle){
// start from first. Note that we are setting current = -1.
// as we will be incrementing it in the for loop.
if(current == s1_size - 1 && j < s2_size - 1){
current = -1;
inCycle = true;
}
}
}
if(j == s2_size){
if(inCycle)
return "cyclic";
else
return "regular";
}
}
return "No Occurence";
}
int main()
{
printf("Hello World\n");
char* s1 = "itisaniceday";
char* s2 = "dayitis";
cout<<"Occurence Type: "<<isCyclic(s1, s2)<<endl;
return 0;
}
Here is a solution for the second part of the problem (cyclic part). I simply go through all of the characters in the first string and checked if they are the beginning of a cyclic appearance of the second string.
To check that I used % (the modolu operation) if you don't know what it dose then you really need to learn it now.
Also I used bool instead of int because numbers are confusing (and cursed).
#include <iostream>
#include <string.h>
using namespace std;
#define Max 128
bool isCyclic(char* str1, char* str2);
bool isCyclic(char* str1, char* str2,int start);
int main()
{
char* str1 = new char[Max];
char* str2 = new char[Max];
cout << "Please enter two strings:" << endl;
cin >> str1 >> str2;
cout << isCyclic(str1, str2) << endl;
delete[] str1;
delete[] str2;
}
bool isCyclic(char* str1, char* str2) {
for(int i = 0; i < strlen(str1); i++) {
if(str1[i] == str2[0] && isCyclic(str1,str2,i)) {
return true;
}
}
return false;
}
bool isCyclic(char* str1, char* str2,int start)
{
int containingStrLen = strlen(str1);
for(int i = 0; i < strlen(str2); i++) {
if(str1[(start + i)%containingStrLen] != str2[i]) {
return false;
}
}
return true;
}
There are some things missing in this code still:
1) The first part of the problem (it can easily be derived from this code).
2) Some size validation such as making sure that str1 is bigger then str2 before using is cyclic. And that the strings are smaller then Max (I assume).
3) A proper result print.
Good luck in your exam :)
There's a simple trick : if you duplicate the string's prefix at its own end, the problem becomes a straight substring search as a cyclic match would be recomposed at the end. It also handles the corner case where the substring loops back on itself, such as "looploop" inside of "loop".
So here's how you'd do it in broken C-ish dialect:
bool containsCyclic(char const *string, char const *substring) {
std::size_t const stringLen = std::strlen(string);
std::size_t const substringLen = std::strlen(substring);
// Too long a substring wouldn't fit in the string
if(substringLen > 2 * stringLen)
return false;
// Concatenate `string` with its own substring-long prefix
char *const loopedString = new char[stringLen + substringLen + 1];
std::strcpy(loopedString, string);
{ // Partial reimplementation of std::strncpy(loopedString, string, substringLen)
char const *src = string;
char *dest = loopedString + stringLen;
for(std::size_t count = 0; count < substringLen; ++count)
*dest++ = *src++;
*dest = '\0';
}
{ // Partial and naïve reimplementation of std::strstr(loopedString, substring)
for(char const *start = loopedString; start < loopedString + stringLen; ++start) {
// Check if substring is present at this offset
char const *s1 = start;
char const *s2 = substring;
while(*s2 != '\0' && *s1 == *s2)
++s1, ++s2;
if(*s2 == '\0') {
// We found a complete match of substring inside loopedString
delete[] loopedString;
return true;
}
}
}
// No match found
delete[] loopedString;
return false;
}
And just for kicks, here it is in C++:
bool containsCyclicCpp(std::string const &string, std::string const &substring) {
std::string const loopedString = string + string.substr(0, substring.size());
return loopedString.find(substring) != std::string::npos;
}
See it live on Coliru (with tests!)
//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 ©)
{
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 ©)
{
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;
}
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];
This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 7 years ago.
I tried to overload the + operator on my own C styled version of string class.
Everything is fine except for the display() call for the second string s2 which displays garbage values.
class string_ {
char *str;
int len;
public :
...
void display()
{
cout << str;
}
};
string_ :: string_()
{
str = 0;
len = 0;
}
string_ :: ~string_()
{
delete []str;
len = 0;
}
string_ :: string_(char *s,int l)
{
len = l + 1; // +1 for \0
str = new char[len];
int i;
for(i=0;i<len;i++)
str[i] = s[i];
str[i] = '\0';
}
string_ string_ :: operator +(string_ c)
{
int j = 0, i = 0;
string_ s;
s.len = len + c.len - 1;
s.str = new char[s.len];
while (str[i] != '\0')
{s.str[i] = str[i]; i++;}
while (c.str[j] != '\0')
{s.str[i] = c.str[j]; i++; j++; }
s.str[i] = '\0';
//The below statements gives the desired output
cout <<"\nIN operator +" << str;
cout <<"\nIN operator +" << c.str;
cout <<"\nIN operator +" << s.str;
return s;
}
int main()
{
char *str = "Hello";
char *str1 = " World";
string_ s1(str,5);
string_ s2(str1,6);
string_ s3 = s1 + s2;
cout << "\nstring s1 : ";
s1.display();
cout << "\nstring s2 : ";
s2.display(); //PROBLEM
cout << "\nConcatenated string : ";
s3.display();
return 0;
}
You are missing a proper copy-constructor where you clone the char-array from one string to another.
Also your operator+ should take a const-ref to your string class because a copy is not necessary here.
string_ string_ :: operator +(const string_& c)
The problem
Your 1st object has a pointer to a char-array, e.g. str=0x1. Because you have no copy-constructor the pointer-value is copied over automatically to the copy of your string. Two strings have now the same pointer. The first string deletes the array and the second will fail. Take a look at Rule of three which describes which functions should be implemented to avoid this problem. Edit: R Sahu was faster in the comments.
P.S. Since you use const-literals change char* to const char*.
P.S.2 Please post a MVCE next time.