I have a test class of my to make my own string functions. I have a problem with the copy destructor.
I have 2 strings: s1 and s2.
I call the function s3 = s1 + s2;
It first calls the operator+ function and when it's finished it calls the destructor. Because of this the string object in the operator= function is empty. How can I fix this?
Destructor:
String::~String() {
if (this->str)
delete[] str;
str = NULL;
len = 0;
}
Copy Constructor:
String::String(const String& string) {
this->len = string.len;
if(string.str) {
this->str = new char[string.len+1];
strcpy(this->str,string.str);
} else {
this->str = 0;
}
}
operator=:
String & String::operator= (const String& string) {
if(this == & string)
return *this;
delete [] str;
this->len = string.len;
if(string.str) {
this->str = new char[this->len];
strcpy(this->str,string.str);
} else {
this->str = 0;
}
return *this;
}
operator+:
String& operator+(const String& string1 ,const String& string2)
{
String s;
s.len = string1.len + string2.len;
s.str = new char[string1.len + string2.len+1];
strcpy(s.str,string1.str);
strcat(s.str,string2.str);
return s;
}
operator+ should not return a local variable by reference.
Change the return type of operator+ to String. Ie, make the signature:
String operator+( String const& lhs, String const& rhs )
You probably also want to write a "move constructor" for your String class: String( String&& other ) if you are writing your code in C++11.
A simple move constructor:
String::String( String&& other ): len(other.len), str(other.str) {
other.len = 0;
other.str = nullptr;
}
This isn't required, because the copy in the return statement of your operator+ will probably be "elided" by your compiler under non-trivial optimization levels, but still good practice.
It's calling the Destructor because String s is going out of scope in your operator+ overload. Your operator+ overload needs to be returning a copy instead of a reference.
Therefore you should change your operator+ to
String operator+(const String& string1, const String& string2)
Yeah i got your problem
The thing is when you are returning a reference to a temp object from + operator function and then you are assigning this to other object in main So here = overloaded function gets called in to which you are passing a reference to an object that no longer exists
So either you can return a copy from + operator function
or
you can pass a copy in the = overlaoded function
Related
I have created a basic string class and i want to assign it some value
but the problem is that I have to do something like this...
string str("Hello");
Isn't there a way so i can do it something like....
string str = "Hello";
Like how c++ std:: defined types (vector,string etc) do?
And also I want that instead of typing std::cout << str.val() I can do std::cout << str; to access its value and same for modifying (or updating) it.
#include<iostream>
class string
{
private:
char* str; // An uninitialized string
public:
string(char* text) // Constructor which takes a string
{
str = text;
}
char* val() // used to access the value stored in String
{
return str;
}
void update(char* string2) // used to update the value of string
{
str = string2;
}
};
int main()
{
string myStr("Hello World\n"); //initializes a string object
std::clog << myStr.val();
myStr.update("Bye World\n"); //updates the value of myStr
std::clog << myStr.val();
}
Thanks to whoever answers....
you can do this using overloaded operators
string& operator= ( const string & );
string& operator= ( const char *);
You can Overload the operators = and << like Below
Overloading the = Operator
string& operator = (char * text)
{
this->str=text;
return *this;
}
Overloading the << Operator
friend std::ostream& operator << (std::ostream& outputstream,string& thestr)
{
outputstream<<thestr.val();
return outputstream;
}
So the Overall will be like below
#include<iostream>
class string
{
private:
char* str; // An uninitialized string
public:
string(char* text) // Constructor which takes a string
{
str = text;
}
char* val() // used to access the value stored in String
{
return str;
}
void update(char* string2) // used to update the value of string
{
str = string2;
}
string& operator = (char * text)
{
this->str=text;
return *this;
}
friend std::ostream& operator << (std::ostream& outputstream,string& thestr)
{
outputstream<<thestr.val();
return outputstream;
}
};
int main()
{
string myStr="Hello World\n"; //initializes a string object
std::clog<<myStr;
myStr="Bye World\n"; //updates the value of myStr
std::clog << myStr;
}
You need to implement these operators ('=' and '<<') manually. See https://en.cppreference.com/w/cpp/language/operators (or search for c++ operator overloading in case of broken links)
In this case, you can add this code to your string class.
// Allow assignment with another `string`
string& operator=(const string& other) {
str = other.str;
return *this;
}
// This is optional. Without this, the previous `operator=` will be called
// with `other` implicitly converted to a `string`
string& operator=(char* other) {
str = other;
return *this;
}
// With the `friend` keyword, you can write a global function inside a class
friend std::ostream& operator<<(std::ostream& os, const string& obj)
{
return os << obj.str;
}
I've been writing my own String class and I am not sure how to write operator+ correctly considering I could pass rvalues into it.I think I should have the following 3 non-member functions
String operator+(String &&lhs, String &&rhs);
String operator+(String& lhs,String&&rhs);
String operator+(String&&lhs,String&rhs);
However I am not sure how to implement them. Any help would be appreciated.
First, make sure to define copy and move constructors in your String class:
class String
{
private:
char *m_data;
std::size_t m_length;
...
public:
String();
String(const String &src);
String(String &&src);
~String();
...
};
String::String() :
m_data(nullptr),
m_length(0)
{
}
String(const String &src) :
m_data(new char[src.m_length+1]),
m_length(src.m_length)
{
std::copy_n(src.m_data, m_length, m_data);
m_data[m_length] = 0;
}
String(String &&src) :
m_data(nullptr),
m_length(0)
{
std::swap(m_data, src.m_data);
std::swap(m_length, src.m_length);
}
String::~String()
{
delete[] m_data;
}
Then define operator+ and operator+= for the class:
class String
{
public:
...
String& operator+=(const String &rhs);
...
friend String operator+(String lhs, const String &rhs)
{
lhs += rhs;
return lhs;
}
};
String& String::operator+=(const String &rhs)
{
String tmp;
tmp.m_length = m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(m_data, m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + m_length);
tmp.m_data[tmp.m_length] = 0;
std::swap(m_data, tmp.m_data);
std::swap(m_length, tmp.m_length);
return *this;
}
By taking a const String & as input on the right side, that will handle both lvalue and rvalue inputs.
For operator+, the left-hand side is taken by value so the compiler can decide the best constructor to use based on whether the input is an lvalue (copy) or rvalue (move).
Alternatively, you can implement it to take const String & on the left side so it still handles lvalues and rvalues, but then you have to implement it similar to how operator+= is implemented to avoid the extra allocation of copying lhs before concatenating onto it:
friend String operator+(const String &lhs, const String &rhs)
{
/*
String tmp(lhs);
tmp += rhs;
return tmp;
*/
String tmp;
tmp.m_length = lhs.m_length + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs.m_data, lhs.m_length, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + lhs.m_length);
tmp.m_data[tmp.m_length] = 0;
return tmp;
}
Either way, you should also define a conversion constructor and operator+ for const char * input as well:
class String
{
public:
...
String(const char *src);
...
friend String operator+(const char *lhs, const String &rhs)
{
return String(lhs) + rhs;
/* or:
std::size_t len = std::strlen(lhs);
String tmp;
tmp.m_length = len + rhs.m_length;
tmp.m_data = new char[tmp.m_length+1];
std:copy_n(lhs, len, tmp.m_data);
std:copy_n(rhs.m_data, rhs.m_length, tmp.m_data + len);
tmp.m_data[tmp.m_length] = 0;
return tmp;
*/
}
...
};
String::String(const char *src) :
m_data(nullptr),
m_length(std::strlen(src))
{
m_data = new char[m_length+1];
std::copy_n(src, m_length, m_data);
m_data[m_length] = 0;
}
This will allow concatenating String objects with string literals (String + "literal", "literal" + String, String += "literal", etc).
See operator overloading on cppreference.com for more details.
The way I usually do it is like this:
class foo
{
...
public:
...
foo&& operator +(foo const & other) &&;
foo&& operator +(foo && other) const &;
foo&& operator +(foo && other) &&;
foo operator +(foo const & other) const &;
};
Not sure if Microsoft supports this but this is a good way to do this in more recent standards. Try clang if msvc wont let you.
The advantages of doing it this way are that you get very fine levels of control over the method used. These 4 operations can also be defined outside of the class if needed. But you'll always want 4 for the 4 possibilities of r-value/l-value combinations.
Also, you generally want to qualify l-values as const to indicate that they are not modified.
Simply defining a copy/move constructor is not usually an efficient solution to this problem. You will need a good understanding of how rvalue references work to implement this efficiently.
I'm making my own string class but I got a problem reading the characters of the string while using strlen() to do that.
/****str.h****/
class str
{
private:
char *m_ptr;
unsigned int m_size;
//unsigned int m_capacity;
public:
str();
str (const char *);
str (const str &);
~str (){if (m_size != 0) delete [] m_ptr;};
char *data() {return m_ptr;};
//Sobrecarga de operadors.
str operator=(str);
friend std::ostream& operator<<(std::ostream &, str);
};
I got the error using the constructor initialized with a c-string constant.
/****str.cpp****/
//Default constructor.
str :: str ()
{
m_ptr = new char [1];
m_ptr[0] = '\0';
m_size = 0;
//m_capacity = 10;
}
str :: str(const char *sm_ptr)
{
m_size = strlen(sm_ptr); //HERE IS WHERE THE ERROR OCCURS.
m_ptr = new char[m_size + 1];
strcpy(m_ptr, sm_ptr); //Copies the C string pointed by source into the array pointed by destination, including the terminating null character
}
//Copy constructor.
str :: str(const str &right)
{
m_ptr = new char [right.m_size];
strcpy (m_ptr, right.m_ptr);
m_size = right.m_size;
}
str str::operator=(str right)
{
if (m_size != 0) delete [] m_ptr;
m_ptr = new char [right.m_size + 1];
strcpy(m_ptr, right.m_ptr);
m_size = right.m_size;
return *this;
}
std::ostream &operator<<(std::ostream &strm, str obj)
{
strm << obj.m_ptr;
return strm;
}
0x0053fdd0 {m_ptr=0xcccccccc m_size=3435973836 } str *
Changing the assignment operator declaration to
str& operator=(str&);
or even
const str& operator=(const str&);
would eliminate creation of temporary objects. Check out this article for more information on passing arguments to functions.
There are a couple of other issues. For example in default constructor you allocate memory but you don't set size so it's never going to be freed. Also, in copy constructor and assignment operator it's almost always a good idea to check for self-assignment.
I have several questions about this particular piece of code. I should first mention that pChar will always be a dynamic character array that is resized when needed by methods I have not listed. I have scoured my reference manual and this website but cannot seem to gain a clear understanding of how the & operator works in full. I also do not want to use an alternative as this is an assignment and am not allowed to. I've been at this for days so any help would be appreciated.
What exactly is *this pointing to when using the & operator?
I have been assuming it points to the left operand.
If this is not the case, how do I access the left operand within the & operator method?
If this is all completely incorrect, how can I reach the desired outcome by using the overloaded operator&?
Method for & operator:
PDS_String & PDS_String::operator & (const PDS_String & Str)const
{
PDS_String temp(*this);
strcat(temp.pChar, Str.pChar);
return temp;
}
Main:
void main ()
{
PDS_String String1;
PDS_String String2;
PDS_String String3;
String1 = "monkey";
String2 = "chicken";
String3 = String1 & String2;
// Desired outcome for String3 is "monkeychicken"
}
Class:
class PDS_String
{
public:
PDS_String(); //Default
PDS_String(const PDS_String &); //Copy
~PDS_String(); //Destructor
char * operator & (const PDS_String &) const; // Concatenation
PDS_String & operator = (const char *); // Assignment
// I haven't listed all methods and operator overloads
private:
char * pChar;
long NumChars;
long NumSlots;
};
Assignment operator method:
PDS_String & PDS_String::operator = (const char * Str)
{
if (pChar == Str)
return *this;
else
{
if (NumSlots < strlen(Str))
{
delete[] pChar;
pChar = new char[(strlen(Str) + 1)];
NumSlots = (strlen(Str));
}
else;
}
strcpy(pChar, Str);
NumChars = strlen(Str);
return *this;
}
Thank you very much Matt McNabb. I have changed the operator& method to reflect your suggestion:
char * PDS_String::operator & (const PDS_String & Str)const
{
char * temp;
temp = new char[strlen(pChar)+strlen(Str.pChar)+1];
temp = pChar;
strcat(pChar, Str.pChar);
return temp;
}
*this refers to the left-hand operand. this points to the left-hand operand.
N/A
Stop returning a reference to a local object. temp ceases to exist when the function ends, so the caller has a dangling reference (causing undefined behaviour).
The normal semantics for a binary operator is to return an object by value. So you could change to PDS_String PDS_String::operator & (const PDS_String & Str)const and everything will be fine.
Usually what's done is that the operator+= function is a member function that changes *this, and then you have a free function X operator+(X x1, X x2) { return x1 += x2; }. You could do the same thing with operator& and operator&=.
You also have no protection against somebody trying to concatenate a long string and overflowing however much memory is allocated to pChar, you will need to add checks for that.
I just need a little help on this assignment. I have to redefine operators to work with strings. I'm starting with the == operator and I have it declared in my header file, however when I go to define the function in my cpp file, it says it's incompatible with the declared function. It's probably a stupid mistake, I just don't understand this sometimes.
string.h header file
#pragma once
#include <iostream>
#include <string>
using namespace std;
#define NOT_FOUND -1
// C++ String class that encapsulates an ASCII C-string
class String
{
public:
// Default constructor
String();
// MUST HAVE: Copy-constructor that performs deep copy
String(const String& source);
// Init-constructor to initialize this String with a C-string
String(const char* text);
// Init constructor, allocates this String to hold the size characters
String(int size);
// Destructor
~String();
bool& compareTo(const String& cmp1);
// Assignment operator to perform deep copy
String& operator = (const String& source);
// Assignment operator to assign a C-string to this String
String& operator = (const char* text);
// Returns a reference to a single character from this String
char& operator [] (int index) const;
// Comparison operators
bool operator == (const String& compareTo) const;
string.cpp file
#include "string.h"
#include <string>
#include <sstream>
using namespace std;
// Default constructor
String::String()
{
Text = NULL;
}
// MUST HAVE: Copy-constructor that performs deep copy
String::String(const String& source)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = source;
}
// Init-constructor to initialize this String with a C-string
String::String(const char* text)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = text;
}
// Init constructor, allocates this String to hold the size characters
String::String(int size)
{
Text = new char[size];
}
// Destructor
String::~String()
{
delete[] Text;
}
// Assignment operator to perform deep copy
String& String::operator = (const String& source)
{
// Call the other assigment operator to perform deep copy
*this = source.Text;
return *this;
}
// Assignment operator to assign a C-string to this String
String& String::operator = (const char* text)
{
// Ddispose of old Text
delete[] Text;
// +1 accounts for NULL-terminator
int trueLength = GetLength(text) + 1;
// Dynamically allocate characters on heap
Text = new char[trueLength];
// Copy all characters from source to Text; +1 accounts for NULL-terminator
for ( int i = 0; i < trueLength; i++ )
Text[i] = text[i];
return *this;
}
***bool& String::operator ==(string cmp2)***
{
};
Your compareTo declaration has const while definition has no const, which means they have definition has different signature with declaration:
bool& compareTo(const String& cmp1);
^^^
bool& String::compareTo(string cmp2)
{
};
BTW, why does your compareTo return bool& ?
Also should avoid using namespace std; in any header files. see why-is-using-namespace-std-considered-a-bad-practice-in-c