Overloading operator +. String class - c++

Im writing string class by myself. And I overloaded + operator. Its works fine, but then I tried to eguate cstr = str +pop , its did nothing. `You could see my error in main() function. Complier doesnt give any mistake.
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
class S {
public:
S();
S(const char *str);
S(const S &s);
~S() { delete []string;}
S &operator =(const S &s);
int lenght() const {return l ;}
char* strS() const {return string;}
friend ostream &operator <<(ostream &, const S &first) {cout<<first.string;}
friend S operator+ (const S& first, const S& second);
private:
char *string;
int l;
};
int main(){
S pop("Q6");
S str("M5");
S cstr = str +pop; // works correct
cout<<str;
str = str + pop;
cout<<str ; // doesnt work, it doesnt write in terminal
return 0;
}
S::S()
{
l = 0;
string = new char[1];
string[0]='\0';
}
S::S(const char *str)
{
l = strlen(str);
string = new char[l+1];
memcpy(string, str, l+1);
}
S::S(const S &s)
{
l = s.l;
string = new char[l+1];
memcpy(string,s.string,l+1);
}
S &S::operator=(const S &s)
{
if (this != &s)
{
delete []string;
string = new char[s.l+1];
memcpy(string,s.string,s.l+1);
return *this;
}
return *this;
}
S operator +(const S& first, const S& second)
{
S temp;
temp.string = strcat(first.strS(),second.strS());
temp.l = first.lenght() + second.lenght();
return temp;
}
I`m looking forward to your help.

Your operator has bugs!
S temp;
//^^^^ has only one byte buffer!!!
temp.string = strcat(first.strS(),second.strS());
// 1 byte ^^^^^ strcat appends second.strS to first.strS
You should re-allocate memory for temp:
S temp;
temp.l = first.lenght() + second.lenght();
delete [] temp.string; // !!!! -
temp.string = new char[temp.l + 1]; // !!!!
// you should have another c-tor which can allocate memory!!!
// like: S(unsigned length, unsigned char c = '\0')
strcpy(temp.string, first.strS());
strcat(temp.string, second.strS());
Besides this obvious bug - you should also take care of exceptions - std::bad_alloc for example. Look at copy-and-swap idiom for better approach for this task.

From the manpage for strcat:
The strcat() and strncat() functions append a copy of the null-terminated
string s2 to the end of the null-terminated string s1, then add a termi-
nating `\0'. The string s1 must have sufficient space to hold the
result.
You're using it as if it allocates room for a new char array, then fills it. But, it doesn't do that.

The problem is that your operator+ doesn't allocate any memory for the combined string. Nor does it copy the string to right place (it copies the string to first, not to temp). There's no easy fix with the class design you have.

The problem is with your implementation of operator+. strcat() appends the string ponted by the second argument to the string pointed by the first argument. The return value is the first argument. Therefore on return from operator+ the resulting S and the first S argument will be pointing to the same buffer. Which will later be deleted twice...

Check the description of strcat. It appends the second argument to
the first, supposing both are null terminated strings, and returns the
first argument. In your case:
it appends to the string member of first, although there isn't
enoguh memory for it (undefined behavior), and
it sets the string pointer in temp to point to the same memory as
that in first; the first one to be destructed leaves the other
pointing to deleted memory, and the memory allocated in the default
constructor of temp is leaked.
Also, you never terminate your strings with '\0', so strcat may do
just about anything.
A better solution would be to implement += first, and define + in
terms of it. += would have to grow the memory it has, and append the
text from the second string to it.
And while I'm at it: your operator= doesn't work either. It will
leave the object in a state where it cannot be destructed if the new
fails (throwing std::bad_alloc). You must ensure that all operations
that can fail occur before the delete. (The fact that you need to
test for self assignment is a warning sign. It's very rare for this
test to be necessary in a correctly written assignment operator.) In
this case, the swap idiom would probably be your best bet: copy
construct a new S in a local variable, then swap their members.

Related

how do functions read pointers?

im new to c++. I made this simple program using classes which does simple stuff like change instances of objects and copying objects.
But I'm confused how functions like std::strlen() read pointers.As far as i know pointer is just an address to another variable.If i pass in derefrenced pointer in std::strlen(*c) i get some error saying compiler cannot convert pointer to char, but if i pass in raw pointer like std::strlen(c)
the code compiles fine and gives the desired output.Why is that?std::strlen() should know the exact string to calculate length right?how does passing pointer help?
Same goes with strcpy_s() if i pass in derefrenced pointers like this strcpy_s(*str,strlen(str)+1,*c) it wont work but if i pass in pointers like strcpy_s(str,strlen(str) + 1, c), it works perfectly.
Please help me out as I'm really confused.Thanks
here is the code-
#include <iostream>
#include <cstring>
#include <vector>
class mystring
{
char* str;
public:
//constructors
mystring();
mystring(const char* c);
mystring(const mystring& original);
~mystring();
//methods
void display() const;
void length() const;
void display_vector(const std::vector <mystring>& thing) const;
//operator overloading
mystring& operator= (const mystring& source);
};
mystring::mystring() //default constructor
:str(nullptr)
{
str = new char[1];
*str = '\0';
}
mystring::mystring(const char* c) //overloded constructor (char *c)
{
str = new char[std::strlen(c) + 1];
strcpy_s(str, std::strlen(c) + 1, c);
}
mystring::mystring(const mystring& original) // copy constructor
{
str = new char[strlen(original.str) + 1];
strcpy_s(str, strlen(original.str) + 1, original.str);
}
mystring::~mystring() //destructor
{
delete[] str;
}
void mystring::display() const // display method for mystring
{
std::cout << str << std::endl;
}
void mystring::length() const // length fuc
{
std::cout << strlen(str) << std::endl;
}
mystring& mystring::operator= (const mystring& source) //= operator overload
{
delete[] this->str;
this->str = new char[std::strlen(source.str) + 1];
strcpy_s(this->str, std::strlen(source.str) + 1, source.str);
return *this;
}
int main()
{
mystring v{ "v_and_jackie" };
v.display();
mystring jackie;
jackie = v;
jackie.display();
std::vector <mystring> thing;
thing.push_back("wake up");
thing.push_back("samurai");
for (const auto i : thing)
i.display();
}
Thanks again.
std::strlen receives as an argument a pointer to the beginning of the string:
std::size_t strlen( const char* str );
Returns the length of the given byte string, that is, the number of characters in a character array whose first element is pointed to by str up to and not including the first null character. The behavior is undefined if there is no null character in the character array pointed to by str.
https://en.cppreference.com/w/cpp/string/byte/strlen
As far as i know pointer is just an address to another variable
Correct.
If i pass in derefrenced pointer in std::strlen(*c) i get some error saying compiler cannot convert pointer to char
Read the message again. It should say that it cannot convert a char to a pointer-to-char.
but if i pass in raw pointer like std::strlen(c) the code compiles fine and gives the desired output.Why is that?
This is because of type safety that C++ language has. std::strlen accepts an argument that is a pointer to char. If you pass the function something that isn't a pointer to char nor convertible to such pointer, like a char object for example, then the program is ill-formed, and you'll get an informative message pointing out your mistake.
When you indirect through a pointer, what you get back is not a pointer of same type. Instead, the value that you get back is the pointed object, which always has a different type. In case of pointer to char, the pointed object is a char.
how does passing pointer help?
Passing an argument of correct type into a function call helps.
I'm gonna try and explain the reasoning behind why you have to pass a char:
Back in the days memory was very valuable. And instead of using memory for an Integer to keep track of the string length a convention was used: End the string with a null-byte (zero).
Every character is saved in the ASCII format (lookup ASCII table).
Because every string is null-terminated "Hello" would look like {72, 101, 108, 108, 111, 0} in memory.
So to get the length of the string you need a pointer to the first character, then advance forwards until you get to the null-byte. By counting how many bytes you advanced in memory you know the amount of characters contained in the string.
This is exactly what strlen does.
By dereferencing the pointer to the first character you get the first character.
This way strlen would not know where the first character came from in memory and cannot read the next character.
So you can think of dereferencing the pointer to a string (the first char) as loosing all information about that string except what the first character is.
Hopefully this was understandable.

How can I convert a string of characters to an object type?

I've wanted to create a program using the operator new in order to obtain the right amount of memory for a string of characters.
#include <iostream>
#include <cstring>
using namespace std;
class String
{
private:
char* str;
public:
String(char* s)
{
int len = strlen(s);
str = new char[len + 1]; // points to a memory
strcpy(str, s);
}
~String()
{
cout << "Deleting";
delete[] str;
}
void display()
{
cout << str << endl;
}
};
int main()
{
String s1 = "who knows";
cout << "s1=";
s1.display();
return 0;
}
The constructor in this example takes a normal char* string as its argument. It obtains space in
memory for this string with new; str points to the newly obtained memory. The constructor
then uses strcpy() to copy the string into this new space. Of course, I've used a destructor as well.
However, the error is: no suitable constructor exists to convert from const char[10] to "String".
I'm a total beginner when it comes to pointers and I'm trying to understand why my constructor doesn't work as intended.
As noted in the comments, some compilers will accept your code (depending on how strict they are). For example, MSVC will accept it when "conformance mode" is disabled - specifically, the /Zc:strictStrings option.
However, to fully conform to strict C++ rules, you need to supply a constructor for your String class that takes a const char* argument. This can be done readily by just 'redirecting' that constructor to the one without the const keyword, and casting away the 'constness':
String(const char* cs) : String(const_cast<char*>(cs)) { }
An alternative (and IMHO far better) way is simply to add the const qualifier to your existing constructor's argument, as all the operations you do therein can be be done perfectly well with a const char* (you would then not actually need the non-const version):
String(const char* s) {
int len = strlen(s);
str = new char[len + 1]; // points to a memory
strcpy(str, s);
}
Without one or other of these 'amendments' (or something equivalent), you are passing the address of string literal (which is immutable) to a function (the constructor) that takes an argument that (in theory, at least) points to data that could be changed within that function; thus, a strict compiler is within its 'rights' to disallow this. As your constructor doesn't change the data, then you should have no problem qualifying its argument as const.

C++ weird behaviour with stack variables and functions

I've got a String class with a char* buffer and a unsigned int length.
The string class has two constructors:
String(const char* str);
String(const String& other);
and a destructor
~String()
which deletes the char array with delete[] buffer;.
Both constructors create a new buffer array buffer = new char[size]; and fill it with the correct data from either the const char* string or the const String& other. The string buffer is null-terminated.
In my main function I've got the following code:
int main() {
String a("Hello");
a = a.Replace('l', 'p');
printf("%s\n", a.char_ptr());
}
I would expect it to print Heppo to the console. The replace function takes two characters where all occurrences of the first one are replaced by the second one. It returns an entirely new String:
String String::Replace(const char& a, const char& b) {
const char* buf = ...;
// Do the replacement and store the result in buf
String s(buf);
delete[] buf;
return s;
}
From my understanding, the compiler will return a copy of the local variable s. Because of that, the a variable in main() should be a perfectly legitimate String. But the output to the console looks like ¦¦¦¦¦¦¦¦¦¦, which looks like uninitialized memory.
Even weirder, when i change my main method to:
int main() {
String a("Hello");
String b = a.Replace('l', 'p');
printf("%s\n", b.char_ptr());
}
I see the expected output. After a lot of reading I could not figure out the solution, as this is problem is really hard to describe in a google/stackoverflow search.
the main problem is violation of the rule of big 3. since you have a none trivial destructur, you must also define a copy constructor and an assignment operator.
you may consider the copy-swap idiom in implementing the above functions.
Not definining either of the two in presence of a none-trivial destructor, leads to resource(memory in this sample) leak.

code in addition overload having no effect

I'm working through an old book of C++ at the moment, and in it was developed a "rational numbers" class to introduce the idea of operator overloading, etc. Here's some example code from the book:
interface:
const Rational operator+(const Rational& Rhs) const;
implementation:
const Rational Rational::operator+(const Rational& Rhs) const
{
Rational Answer(*this);
Answer += Rhs;
return Answer;
}
The copy constructor does what you think it would do, and the += operator is correctly overloaded.
I decided to practice a bit by implementing a string class, and so I took a similar approach. My += overload works fine, but the + seems to have no effect ultimately.
interface:
const String operator+(const String&) const;
implementation:
const String String::operator+(const String& Rhs) const
{
String Answer (*this);
Answer += Rhs;
return Answer;
}
where the copy constructor (which works) is defined as such:
String::String(const String& str)
{
unsigned _strlen = str.len() + 1;
content = new char[_strlen];
std::memcpy(content, str.content, _strlen);
length = _strlen - 1;
content[length] = '\0';
}
and += is overloaded by the following:
const String& String::operator+=(const String& Rhs)
{
unsigned _Addl = Rhs.len();
unsigned newLen = _Addl + length; //length is member variable -- current length of content
content = (char*) realloc( content, newLen+1 );
std::memcpy(content+length, Rhs.content, _Addl);
content[newLen] = '\0';
return *this;
}
However -- while I can get proper output for +=, the + operator is failing to actually return a concatenated string. With debugging outputs inside of the function, Answer is holding the correct content, but it returns the original String instead of the concatenated one. I have a feeling this is something to do with const's being everywhere, but I've tried without it as well to no good fortune.
Test Code:
(in main):
String s1 ("Hello");
String s2 (" World!");
String s3 = (s1+s2); //prints "Hello" when s3 is output
cout << (s1+s2) << endl; //prints "Hello"
String constructor for const char*
String::String(const char* str)
{
unsigned _strlen = strlen(str) + 1;
content = new char[_strlen];
std::memcpy(content, str, _strlen);
length = _strlen - 1;
content[length] = '\0';
}
Your String::operator+=(), despite your claim it is properly implemented, is not properly implemented.
Firstly, realloc() returns NULL if it fails, and your code is not checking for that.
Second, and more critical, is that the length member is not being updated. Since your code calls the len() member function to get the length of one string, and uses the length member to get the length of the other, all of your functions need to ensure those two methods are in sync (i.e. that they give consistent results for a given instance of String). Since length is not being updated, your code does not ensure that.
There are probably better approaches than using C-style memory allocation as well, but (assuming this is a learning exercise) I'll leave that alone.
You've given no pertinent code for your Rational class but, if it is not working, your code presumably exhibits similar inconsistencies between what various constructors and member functions do.

Avoiding memory leak

So I was learning OOP in C++ and I thought it would be a good practice to write my own string class (for learning purposes, of course). I came up with a problem which I didn't know how to solve. Here's some peace of code:
class String {
char *str;
public:
String(char const *str);
~String();
String operator + (char const *str);
};
String::String(char *str) {
this->str = _strdup(str);
}
String::~String() {
free(this->str);
}
String String::operator+(char const *str) {
char *temp = (char *) malloc(strlen(str) + strlen(this->str) + 1);
strcpy(temp, this->str);
strcat(temp, str);
return temp;
}
The problem here is, that this piece of code will cause a memory leak. Return from "operator +" invokes my constructor, which copies temp by allocating more memory and I couldn't find any way how can I free it.
Your operator + is defined as returning a String but you're returning a char* which means the compiler is implicitly converting it using the constructor. This copies the string but doesn't free the original which you are therefore leaking.
There are lots of things you could do to improve the code, as others have suggested, but to fix the actual leak you could do this:
String String::operator+(char const *str) {
char *temp = (char *) malloc(strlen(str) + strlen(this->str) + 1);
strcpy(temp, this->str);
strcat(temp, str);
String strTmp(temp);
free(temp);
return strTmp;
}
Writing a string class is not a simple task, if you want to do it right. For the particular problem that you are facing I can make a couple of recommandations...
Implement append() or operator+= that creates a larger buffer copies the contents, swaps the internal buffer and the newly created and releases the old one.
Then operator+ becomes a trivial task:
String operator+(String lhs, String const & rhs) {
lhs += rhs; // lhs.append(rhs);
return lhs;
}
(Of course, this is assuming that you provide correct definitions of the copy constructor, and assignment operator)
You forgot to implement operator= and the copy constructor. If you don't provide your own, the compiler will implement them for you doing a member wise copy which causes your memory leak.