What's wrong with my String concatenation in C++? - c++

I am learning C++ by creating a String class for an embedded project and I have a problem with my String class' concatenation.
Here is my main method and the output
#include <iostream>
#include "string.hpp"
using namespace std;
int main() {
String s1("hello "), s2("world");
String s3 = s1 + s2;
cout << "s1=" << s1 << endl;
cout << "s2=" << s2 << endl;
cout << "s3=" << s3 << endl;
return 0;
}
s1=hello
s2=world
s3=hello
Rather than print out "hello world", it prints just "hello "
Here is my string.hpp class:
#pragma once
#include <cstring>
#include <iostream>
class String {
public:
String() : c_str(NULL), len(0)
{
}
String(const String& str) : c_str(new char[str.len]), len(str.len)
{
strncpy(c_str, str.c_str, len);
c_str[len] = '\0';
}
String(const char* str) : String(str, strlen(str))
{
strncpy(c_str, str, len);
c_str[len] = '\0';
}
String(const char* str, const int n) : len(n), c_str(new char[n+1])
{
strncpy(c_str, str, len);
}
~String()
{
delete[] c_str;
}
const char* get_c_str()
{
return c_str;
}
bool contains(const String &cmd, const size_t pos)
{
return strncmp(c_str+pos, cmd.c_str, cmd.len) == 0;
}
size_t length()
{
return len;
}
friend std::ostream& operator<<(std::ostream& os, const String obj)
{
os << obj.c_str;
return os;
}
friend void swap(String& s1, String& s2)
{
using std::swap;
swap(s1.c_str, s2.c_str);
swap(s1.len, s2.len);
}
bool operator==(const String& str)
{
return strncmp(c_str, str.c_str, len) == 0;
}
char operator[](const size_t i)
{
return c_str[i];
}
String& operator=(const String& src)
{
String tmp(src);
swap(*this, tmp);
return *this;
}
String operator+(const String& rhs)
{
const size_t new_len = len + rhs.len;
char* new_c_arr = new char[new_len+1];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs.c_str);
printf("new_c_arr=%s\n", new_c_arr);
return String(new_c_arr, len);
}
String operator+(const char* rhs)
{
const size_t new_len = len + strlen(rhs) + 1;
char* new_c_arr = new char[new_len];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs);
return String(new_c_arr, new_len);
}
private:
char* c_str;
int len;
};
I read about "the big 3" on SO while looking for a similar question and not sure if it because of this.

This code is broken in numerous ways.
#pragma once
Actual include guards are more portable than #pragma once.
String() : c_str(NULL), len(0)
{
}
Your default constructor makes c_str null; your other functions never check for this case. Remember that even an empty C string has one character.
String(const String& str) : c_str(new char[str.len]), len(str.len)
{
strncpy(c_str, str.c_str, len);
c_str[len] = '\0';
}
You only allocated str.len characters for c_str, yet you are accessing c_str[len].
String(const char* str) : String(str, strlen(str))
{
strncpy(c_str, str, len);
c_str[len] = '\0';
}
The constructor to which you delegated already performed a copy. Why are you calling strncpy here again?
String(const char* str, const int n) : len(n), c_str(new char[n+1])
{
strncpy(c_str, str, len);
}
and here you didn't make sure that your string is null-terminated.
const char* get_c_str()
{
return c_str;
}
Should be marked const.
bool contains(const String &cmd, const size_t pos)
{
return strncmp(c_str+pos, cmd.c_str, cmd.len) == 0;
}
Ditto. And you didn't check that pos is in range.
size_t length()
{
return len;
}
const.
friend std::ostream& operator<<(std::ostream& os, const String obj)
{
os << obj.c_str;
return os;
}
obj should be passed by const reference, not by value.
bool operator==(const String& str)
{
return strncmp(c_str, str.c_str, len) == 0;
}
const again, and the logic isn't even correct. By this logic, "something" == "something else" since you are only comparing the first len characters.
char operator[](const size_t i)
{
return c_str[i];
}
If you are returning a copy, then it should be const. If you want to allow users to modify the characters stored in the string, then it should return a char &. (Even better, have two separate overloads, one const and one non-const.)
String operator+(const String& rhs)
{
const size_t new_len = len + rhs.len;
char* new_c_arr = new char[new_len+1];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs.c_str);
printf("new_c_arr=%s\n", new_c_arr);
return String(new_c_arr, len);
}
You are constructing the new string with the wrong length, and also leaking new_c_arr. And this function should be const (and really should be implemented in terms of operator+=, which you don't have).
String operator+(const char* rhs)
{
const size_t new_len = len + strlen(rhs) + 1;
char* new_c_arr = new char[new_len];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs);
return String(new_c_arr, new_len);
}
Leaking new_c_arr again; also, new_len here includes the null terminator, while the version in the other operator+ doesn't. Your constructor taking a length does not appear to include the null terminator as part of the length.

This is the line of problem in the method String operator+(const String& rhs):
return String(new_c_arr, len);
You need to changed to this:
return String(new_c_arr, new_len+1);
You are initializing the returning String with a length equal to the first part only. Not the whole concatenated string.
See that the other operator is OK.
By the way you're creating a lot of new char[] in the operator+ and the constructor.
In the case of the operator+ you are creating a new char[] in every call and passing to the constructor of string (not deleting after), and in the constructor are creating a new char[] to storage the string that is deleted in the destructor, the new char[] created in the operators are leaked.

Related

What's wrong, why is the "copy assignment operator" invoked right after "copy constructor"?

[NOTE: My first attempt to implement such class, so please, don't throw stones at me and please don't be rude. Be patient. Thanks.]
So, I have written a custom MyString Class in C++.
Running version can be found here: https://onlinegdb.com/rJMEzP9aD.
I got some strange behaviors. I am trying to get the copy constructors, copy operator and the const char* data() to work together.
EDIT #4: This is strange,
class MyString {
public:
MyString(): pSize{0}, pStr{nullptr} {}
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(cstr, cstr + pSize, pStr);
}
MyString(const MyString& rhs):
pSize{rhs.size()}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
}
~MyString()
{
if(pStr){
delete[] pStr;
}
}
size_t size() const { return pSize; }
size_t capacity() const { return pCapacity; }
const char* data() const { return pStr; }
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
if(pCapacity < rhs.size()){
delete[] pStr;
pSize = rhs.pSize;
pCapacity = (pCapacity < rhs.capacity()) ? rhs.capacity() : pCapacity;
pStr = new char[pCapacity];
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
return *this;
}
pSize = rhs.size();
pCapacity = rhs.capacity();
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
return *this;
}
private:
size_t pSize;
char* pStr;
size_t pCapacity = 14;
inline size_t compute_length(const char* cstr)
{
if(cstr == "")
return 0;
size_t i = 0;
while(cstr[++i] != '\0');
return i;
}
}
Main.cpp,
int main()
{
MyString s("Hello");
//assert(s == "Hello");
cout << s << endl;
s = "";
cout << s << endl;
assert(s == "wow");
return 0;
}
This class supports what a regular String class supports, NOT everything, and obviously far from perfect. However, there's a const char* data() const; which returns a pointer. So, in case the previous class modifies the content of its pointer, the other pointer must also be affected. Like the STD class does. However, for my implementation this comes with some cost. Here I have no idea currently why I have the issues I have. For example: performing s = "something new";, doesn't seem to replace previous. Or, copy/move to previous allocated memory pointer e.g. when sizes of a string and local heap allocated string match vice versa.
compute_length function would return 1 when used on an empty string (try to increment iinside the loop).
Try to write your operators and constructors for char* not only for MyString
The == operators seem not to be complete.
I didn't read the code in detail, so I may have missed important stuff.
Some changes
Always store the string null terminated. It'll help interacting with other APIs.
Two friends added for std::ostream and ==.
Memory cleanup.
Capacity always set to largest allocation seen during the class lifetime.
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
class MyString {
public:
MyString() : MyString(""){}
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{new char[pSize+1]}, pCapacity{pSize}
{
std::copy_n(cstr, pSize + 1, pStr);
}
MyString(const MyString& rhs):
pSize{rhs.size()}, pStr{new char[pSize+1]}, pCapacity{pSize}
{
std::copy_n(rhs.data(), rhs.size()+1, pStr);
}
~MyString()
{
delete[] pStr;
}
size_t size() const noexcept{ return pSize; }
size_t capacity() const noexcept{ return pCapacity; }
const char* data() const noexcept{ return pStr; }
char* data() noexcept { return pStr; }
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
pSize = rhs.size();
if(pCapacity < rhs.size()){
delete[] pStr;
pStr = nullptr;
pStr = new char[pSize+1];
pCapacity = pSize;
}
std::copy_n(rhs.data(), rhs.size() + 1, pStr);
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const MyString& string)
{
os.write(string.pStr, string.size());
return os;
}
friend bool operator==(const MyString& lhs, const MyString& rhs)
{
return std::strcmp(lhs.pStr, rhs.pStr) == 0;
}
private:
size_t pSize{};
char* pStr{};
size_t pCapacity{};
static inline size_t compute_length(const char* cstr)
{
size_t i = 0;
while(cstr[i] != '\0')++i;
return i;
}
};
int main()
{
using std::cout;
using std::endl;
MyString s("Hello");
assert(s == "Hello");
cout << s << endl;
s = "wow";
cout << s << endl;
assert(s == "wow");
return 0;
}

What's wrong with my custom string class operator+?

I'm trying to make my own String class, and I have a problem with operator+.
When I use + as " s1 = s2 + s3 " it works.
But in case just use " s1 + s1 " or " s2 + s3 " does not work.
When I run it, I get 0xC0000005 error on 'init' function at delete[] str_; line.
I want to know what's wrong with my codes...
this is my string.h
class String
{
private:
int len_;
int capa_;
char* str_;
public:
String(const String& str);
String(const char* s = "");
~String();
const char* Print(bool show = true);
int size();
int length();
int capacity();
String& assign(const String& str);
String& assign(const char* s);
String& operator=(const String& str);
String& operator=(const char* s);
String& append(const String& str);
String& append(const char* s);
String& operator+=(const String& str);
String& operator+=(const char* s);
String operator+(const String& str);
String operator+(const char* s);
char& operator[](int index);
void shrink_to_fit();
friend std::ostream& operator<<(std::ostream& os, const String& str);
private:
void init(const char* s);
String plus(const char* s);
};
this is my string.cpp
void String::init(const char* s)
{
if (str_ == s) {}
else
{
delete[] str_;
}
len_ = strlen(s);
capa_ = len_ + 1;
str_ = new char[capa_];
strcpy_s(str_, capa_, s);
}
String::String(const String& str)
{
init(str.str_);
}
String::String(const char* s)
{
init(s);
}
String::~String()
{
delete[] str_;
}
int String::size()
{
return this->len_;
}
int String::length()
{
return strlen(this->str_);
}
int String::capacity()
{
return this->capa_;
}
String& String::assign(const String& str)
{
assign(str.str_);
return *this;
}
String& String::assign(const char* s)
{
init(s);
return *this;
}
String& String::operator=(const String& str)
{
assign(str.str_);
return *this;
}
String& String::operator=(const char* s)
{
assign(s);
return *this;
}
String& String::append(const String& str)
{
append(str.str_);
return *this;
}
String& String::append(const char* s)
{
len_ = this->len_ + strlen(s);
capa_ = len_ + 1;
char* temp = new char[capa_];
strcpy_s(temp, capa_, this->str_);
if (str_ == s) {}
else
{
delete[] str_;
}
strcat_s(temp, capa_, s);
str_ = new char[capa_];
strcpy_s(str_, capa_, temp);
delete[] temp;
return *this;
}
String& String::operator+=(const String& str)
{
append(str);
return *this;
}
String& String::operator+=(const char* s)
{
append(s);
return *this;
}
char& String::operator[](int index)
{
int index_;
if (index < 0)
{
index_ = 0;
}
else if (index > this->len_)
{
index_ = this->len_ - 1;
}
else
{
index_ = index;
}
return str_[index_];
}
String String::plus(const char* s)
{
int tempcapa = this->len_ + strlen(s) + 1;
char* temp = new char[tempcapa];
strcpy_s(temp, tempcapa, this->str_);
strcat_s(temp, tempcapa, s);
return temp;
}
String String::operator+(const String& str)
{
return plus(str.str_);
}
String String::operator+(const char* s)
{
return plus(s);
}
std::ostream& operator<<(std::ostream& os, const String& str)
{
os << str.str_;
return os;
}
void String::shrink_to_fit()
{
if (this->capa_ > this->len_ + 1)
{
char* temp = new char[len_ + 1];
strcpy_s(temp, len_ + 1, this->str_);
delete[] this->str_;
this->str_ = new char[len_ + 1];
strcpy_s(this->str_, len_ + 1, temp);
delete[] temp;
this->capa_ = len_ + 1;
}
}
const char* String::Print(bool show)
{
if (show == true)
std::cout << str_;
return str_;
}

Error with friend non-member function which gives a segmentation fault

For this project we are suppose to cover the topic of dynamic memory allocation and deallocation. The functionality of the 'operator<<; is to output(to terminal or file depending on the type of ostream& os object passed as a parameter to it) the MyString data (the C-string representation held within m_buffer). Right after my program prints to the Terminal "Access Successful." I receive a Segmentation fault (core dumped) I am quite sure that it have something to do with the operator << but I do not know how to fix this. Hence, it fails to print "Testing Operator <<()". Any Suggestions? Thank you!
//int main
int main(){
std::cout << "Testing operator[]()" << std::endl;
MyString ms_access("Access successful (NOT)");
ms_access[17] = 0;
std::cout << "Testing operator<<()" << std::endl;
cout << ms_access << endl;
}
class MyString{
public:
MyString();
MyString(const char * str);
MyString(const MyString & other_myStr);
~MyString();
size_t size() const;
size_t length() const;
const char * c_str() const;
bool operator== (const MyString & other_myStr) const;
MyString & operator= (const MyString & other_myStr);
MyString operator+ (const MyString & other_myStr) const;
char & operator[] (size_t index);
const char & operator[] (size_t index) const;
friend std::ostream & operator<<(std::ostream & os, const MyString & myStr);
private:
void buffer_deallocate();
void buffer_allocate(size_t size);
char * m_buffer;
size_t m_size;
};
//definition
std::ostream& operator<<(std::ostream& os, const MyString& myStr){
//if(os == std::cout){
os << myStr.m_buffer << std::endl;
//}
}
//will allow by-reference accessing of a specific character at index size_tindexwithin the allocated m_bufferchar arrayof a non-constqualified object.
char& MyString::operator[](size_t index){
size_t counter = 0;
while(counter != index){
counter++;
}
return m_buffer[counter];
}
//will allow by-reference accessing of a specific character at index size_tindexwithin the allocated m_bufferchar array of a constqualified object.
const char& MyString::operator[](size_t index)const{
size_t counter = 0;
while(counter != index){
counter++;
}
return m_buffer[counter];
}
void MyString::buffer_deallocate(){
if(m_buffer != NULL){
delete [] m_buffer;
}
}
void MyString::buffer_allocate(size_t size){
if(m_buffer != NULL){
buffer_deallocate();
}
m_size = size;
m_buffer = new char[m_size];
}
MyString::MyString(){
m_size = 0;
m_buffer = NULL;
}
MyString::MyString(const char *str){
m_buffer = NULL;
m_size = strlen(str);
buffer_allocate(m_size);
strcpy(m_buffer,str);
}
MyString::MyString(const MyString &other){
m_buffer = NULL;
m_size = other.m_size;
buffer_allocate(m_size);
strcpy(m_buffer,other.m_buffer);
}
size_t MyString::size()const{
return m_size;
}
size_t MyString::length(){
return strlen(m_buffer)-1;
}
const char* MyString::c_str() const {
char *str = NULL;
str = new char[m_size];
for(size_t i = 0; i < m_size;i++){
str[i] = *(m_buffer+i);
}
return str;
delete [] str;
str = NULL;
}
bool MyString::operator==(const MyString & other)const{
if(strcmp(m_buffer,other.m_buffer) == 0){
return true;
}else if (strcmp(m_buffer,other.m_buffer) != 0){
return false;
}
}
MyString & MyString::operator=(const MyString & str1){
buffer_deallocate();
m_buffer = new char[str1.m_size];
strcpy(m_buffer,str1.m_buffer);
}
MyString MyString::operator+(const MyString & other_myStr)const{
MyString myStr(strcat(m_buffer,other_myStr.m_buffer));
return myStr;
}

Overriding the subscript operator in String class

I am working on building a string class in C++. Currently I am trying to override the subscript operator so users can get and modify individual characters.
#ifndef STRING_HPP
#define STRING_HPP
#include "test.hpp"
#include <cstring>
#include <iosfwd>
struct String
{
// Defines the npos value.
static constexpr std::size_t npos = -1;
int len;
char *str;
String()
:len(0), str(0){}
String(char const * S)
:len(strlen(S)), str(new char[len + 1]){
assert(S != 0);
strcpy(str, S);
}
~String(){
delete[]str;
}
String (char *S, std::size_t const n)
:len(n), str(new char[len + 1]){
assert(S != 0);
assert(strlen(S) >= n);
strncpy(str, S, n);
str += '\0';
}
String operator +=(String const &S){
int n = len + S.len;
char * p = new char[n+1];
strcpy(p + len, S.str);
len = n;
str = p;
return *this;
}
char* data() const {
return this->str;
}
bool empty() const {
return (this->len == 0);
}
size_t size()const {
size_t temp = len;
return temp;
}
//assignment operator
String operator =(String const &s){
String temp(s);
swap(temp);
return *this;
}
void swap(String &s){
std::swap(len, s.len);
std::swap(str, s.str);
}
size_t find(const char c){
char * p = strchr(this->str, c);
if(p){
return (str - p);
}else{
return npos;
}
}
char &operator[](const size_t pos){
assert(pos >= 0);
assert(pos < this->size());
return str[pos];
//return output;
}
String substr(int index, int dist){
String output((this->str + index) ,dist);
return output;
}
};
// Output
std::ostream& operator<<(std::ostream&, String const&);
//String operator +=(String const &);
bool operator <(String const &, String const &);
bool operator >(String const &, String const &);
bool operator <=(String const &, String const &);
bool operator >=(String const &, String const &);
bool operator ==(String const &, String const &);
#endif
The portion of main that calls the subscript function is:
String s1 = "String";
s1[0] = 'a';
The error I am getting is:
error: passing 'const String' as 'this' argument of 'char& String::operator[](size_t)' discards qualifiers [-fpermissive]
Thanks!
Edit
Updated error message to be correct
turns out the answer is just to add a 'const' at the end of the function:
char &operator[](const size_t pos)const{
assert(pos >= 0);
assert(pos < this->size());
return str[pos];
//return output;
}

Operator+ overloading is not working

I just developed a String class and overloaded =, <<, [] operators but my operator+ is not working, a little help with it please!
Class:
class String
{
private:
int length;
char *chars;
public:
String oprator+(String &obj);
}
Main Code:
int main ( )
{
String str1;
str1 = "This is first text";
String str2;
str2 = "This is second text";
String bigString = str1 + str2;
bigString = bigString + "This is third text";
}
operator+ overload:
String::String operator+ (String &obj)
{
String *temp = new String();
strcpy(temp->chars, chars);
strcat(temp->chars, obj.chars);
return *temp;
}
Your operator+ should read
class String
{
private:
int length;
char *chars;
String(const char* s, size_t n):
length( n ),
chars( new char[n] )
{
std::copy(s, s+length, chars);
}
public:
explicit String(size_t l):
length(l),
chars( new char[l] )
{
}
String(char const* s):
String(s, strlen(s) )
{
}
String(String const& s):
String(s.chars, s.length)
{
}
String& operator=(String s)
{
std::swap(chars, s.chars);
std::swap(length, s.length);
return *this;
}
~String() {delete[] chars;}
template<size_t N> String(const char s[N]):
String(s, N)
{
}
void append(String const& s)
{
char* tmp = new char[length + s.length];
std::copy(chars, chars+length, tmp);
std::copy(s.chars, s.chars + s.length, tmp + length);
delete[] chars;
chars = tmp;
}
template<typename S> friend S& operator<<(S&, String const&);
};
String operator+(String const& s1, String const& s2)
{
String merged(s1);
merged.append(s2);
return merged;
}
template<typename S> S& operator<<(S& stream, String const& s)
{
return stream << s.chars;
}
int main()
{
String s("bla");
std::cout << s << std::endl;
String s2 = s + "bla";
std::cout << s2 << std::endl;
}
You don't want to modify the argument, so it should be a const reference. Making it non-const prevents code like
bigString = bigString + "This is third text";
because the create temporary String, if you add a non-explicit constructor, cannot bind to an l-value reference.
And the operator should not be a member function, but a free function to take advantage of conversions on the first argument. With a free function, you can do
bigString = "This is third text" + bigString;
which is not possible with the member function because char const[] does not have an operator+ which takes a String.
PS: You may want to read Monoliths "Unstrung" for some critic of std::string's interface.