How to append a string at the end of string? - c++

#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
class String
{
private:
char* s;
int size;
public:
String()
{
s = NULL;
size = 0;
}
String(const char* str)
{
size = strlen(str);
s = new char[size];
for (int i = 0; i < size; i++)
s[i] = str[i];
}
String(const String& obj)
{
size = obj.size;
s = new char[size];
for (int i = 0; i < size; i++)
s[i] = obj[i];
}
String(int x)
{
size = x;
s = new char[size];
}
char& operator[](int i)
{
return *(s + i);
}
const char operator[](int i) const
{
return *(s + i);
}
String& operator+(const char& str) // append a char at the end of string
{
int newsize = size + 1;
String newstr(size);
for (int i = 0; i < size; i++)
{
newstr.s[i] = s[i];
}
newstr.s[size] = str;
delete[]s;
s = newstr.s;
size = newsize;
return *this;
}
String& operator+(char*& str) // append a string at the end of string
{
int newsize = size + strlen(str);
String newstr(newsize);
for (int i = 0; i < size; i++)
{
newstr.s[i] = s[i];
}
for (unsigned i = 0; i < strlen(str); i++)
{
newstr.s[size + i] = str[i];
}
delete[]s;
s = newstr.s;
size = newsize;
return *this;
}
operator int() const
{
int m;
m = size;
return m;
}
};
int main()
{
String s1("abcd");
String s2;
s2 = s1 + 'e';
cout << s2[3];
cout << s2[4];
char* c = (char*)"asdfgh";
s2 = s1 + c;
cout << s2[3];
cout << s2[4];
cout << s2[8];
}
My code runs perfectly until the statement char* c = (char*)"asdfgh". After this statement, I am not getting actual output. s2[3] after the statement char* c = (char*)"asdfgh" is supposed to give 'd' in the output, and similarly s2[4] is supposed to give 'a' in the output, and s2[8] is supposed to give 'g' in the output. But, the problem is, when I run this program, I am not getting these actual values.
Kindly look into this and tell me where I need to make changes.

Both of your operator+ implementations are wrong.
You are modifying the String object that is being added to, and then returning a reference to that object. You need to instead return a new String object that is the concatenation of the added-to and added-from String objects, without modifying the added-to String at all. What you have implemented would be more appropriate for an operator+= instead.
See What are the basic rules and idioms for operator overloading? for more details.
That said, there are some other problems with your code, as well. For instance, you are missing a destructor and a copy assignment operator, per the Rule of 3/5/0. You could also consider adding a move constructor and move assignment operator, as well.
Try something more like this:
#include <iostream>
#include <cstring>
#include <utility>
using namespace std;
class String
{
private:
char* s;
size_t size;
public:
String()
{
s = nullptr;
size = 0;
}
String(const char* str)
{
size = strlen(str);
s = new char[size+1];
for (int i = 0; i < size; ++i)
s[i] = str[i];
s[size] = '\0';
}
String(const String& str)
{
size = str.size;
s = new char[size+1];
for (int i = 0; i < size; ++i)
s[i] = str[i];
s[size] = '\0';
}
String(String&& str)
{
size = str.size; str.size = 0;
s = str.s; str.s = nullptr;
}
String(size_t x)
{
size = x;
s = new char[size+1];
s[size] = '\0';
}
~String()
{
delete[] s;
}
void swap(String& other)
{
std::swap(s, other.s);
std::swap(size, other.size);
}
String& operator=(char ch)
{
String newstr(1);
newstr[0] = ch;
newstr.swap(*this);
return *this;
}
String& operator=(const char* str)
{
String(str).swap(*this);
return *this;
}
String& operator=(const String& str)
{
if (this != &str)
String(str).swap(*this);
return *this;
}
String& operator=(String&& str)
{
String(std::move(str)).swap(*this);
return *this;
}
char& operator[](size_t i)
{
return s[i];
}
const char operator[](size_t i) const
{
return s[i];
}
String operator+(char ch)
{
/*
String newstr(*this);
newstr += ch;
return newstr;
*/
String newstr(size + 1);
for (int i = 0; i < size; ++i)
{
newstr[i] = s[i];
}
newstr[size] = ch;
return newstr;
}
String operator+(const char* str)
{
/*
String newstr(*this);
newstr += str;
return newstr;
*/
size_t slen = strlen(str);
String newstr(size + slen);
for (size_t i = 0; i < size; ++i)
{
newstr[i] = s[i];
}
for (size_t i = 0; i < slen; ++i)
{
newstr[size + i] = str[i];
}
return newstr;
}
String& operator+=(char ch)
{
String newstr(size + 1);
for (size_t i = 0; i < size; ++i)
{
newstr[i] = s[i];
}
newstr[size] = ch;
newstr.swap(*this);
return *this;
}
String& operator+=(const char* str)
{
size_t slen = strlen(str);
if (slen > 0)
{
String newstr(size + slen);
for (size_t i = 0; i < size; ++i)
{
newstr[i] = s[i];
}
for (size_t i = 0; i < slen; ++i)
{
newstr[size + i] = str[i];
}
newstr.swap(*this);
}
return *this;
}
size_t getSize() const
{
return size;
}
};
int main()
{
String s1("abcd");
String s2;
s2 = s1 + 'e';
cout << s2[3];
cout << s2[4];
s2 = s1 + "asdfgh";
cout << s2[3];
cout << s2[4];
cout << s2[8];
}

The reason it fails is actually earlier
String s1("abcd");
String s2;
s2 = s1 + 'e';
becuase your operator+ changes its lhs and then returns a reference to itself s2 is now a copy of s1. But they both point to the same 's' char array, things go bang shortly after that.
This would be fixed if you had an operator= that does the same as your copy constructor, but you dont have that
And as Remy points out, your operator+ semantics are wrong anyway. You should return 'newStr'

Related

Failed to concatenation with my custom string class

I have nearly finished creating my own custom string class. However, it seems not going well when the program did not return the output that I was expected. In detail:
Input:
string a = "Hello"
string b = "World!"
Expected output:
HelloWorld!
!dlroWolleH
Actual output:
Hello
Here is my code:
#ifndef _STRING
#define _STRING
#include<iostream>
#include<cstring>
class string {
private:
char* s = nullptr;
unsigned int size = 0;
public:
string();
~string() { delete s; };
string(char* );
string(const char* );
string(const string&);
friend std::ostream& operator << (std::ostream&, string&);
friend string operator +(string, string);
string& operator = (const string&);
string& operator = (const char&);
string& inverse();
char* inconst();
char* output() const{
return s;
}
};
#endif
string::string() :s{ nullptr } {
size = 1;
s = new char[size];
s[0] = '\0';
}
string::string(char* source) {
if (source == nullptr) {
size = 1;
s = new char[size];
s[0] = '\0';
}
else {
size = strlen(source) + 1;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = source[k];
}
}
}
string::string(const char* source) {
if (source == nullptr) {
size = 1;
s = new char[size];
s[0] = '\0';
}
else {
size = strlen(source) + 1;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = source[k];
}
}
}
string::string(const string& t) {
size = t.size;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = t.s[k];
}
}
string& string::operator=(const string& source) {
delete[] s;
size = source.size;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = source.s[k];
}
return *this;
}
string& string::operator=(const char&source) {
const char* t = &source;
if (t == nullptr) {
size = 1;
s = new char[size];
s[0] = '\0';
}
else {
size = strlen(t) + 1;
s = new char[size];
s[size - 1] = '\0';
for (size_t k = 0; k < (size - 1); k++) {
s[k] = t[k];
}
}
return* this;
}
string operator +(string a, string b) {
string t;
t.size = a.size + b.size;
t.s = new char[t.size + 1];
strncpy_s(t.s, a.size + 1, a.s, a.size);
strncpy_s(t.s + a.size, b.size + 1, b.s, b.size);
return t;
}
std::ostream& operator << (std::ostream& os, string& source) {
os << source.output();
return os;
}
char* string::inconst() {
char* t;
t = new char[size + 1];
for (size_t k = 0; k < size; k++)
{
t[k] = s[size - 1 - k];
}
t[size] = '\0';
return t;
}
string& string::inverse() {
this->s = this->inconst();
return*this;
}
int main(){
string a = "Hello";
string b = "World!";
string c = a + b;
std::cout << c << std::endl;
std::cout << c.inverse() << std::endl;
system("pause");
return 0;
}
It seems like that I have caught some errors at concatenation part ( overloading operator + assignment) since I can receive the output i want when i output separated variable a or b like std::cout << b << std::endl; but I cannot find out exactly what I was wrong. Please help me fix my code and thanks for helping me
Your string does store the terminating null character at s[size - 1].
Then in inconst you take the null character not into account, but pretend that size is number of characters in the string.
char* string::inconst() {
char* t;
t = new char[size + 1]; // why +1 here ?
for (size_t k = 0; k < size; k++)
{
t[k] = s[size - 1 - k];
}
t[size] = '\0'; // why another \0 ?
return t;
}
The first iteration assigns s[size-1] to t[0], ie in the end the string stored in t looks like this:
t[0] t[1] ... t[size] t[size+1]
\0 s[size-2] ... s[0] \0
You need to decide if size does count the null terminator or not and then be consistent about that.
Presumably this won't be the last time you need to do debugging, hence I propose to instrument the operator<< to print more information and not rely on the char* overload. Write a loop that prints character by character. If I didnt miss anything and my answer is correct you will see the contents of your string as explained above.
There are more issues in your code, the one I spotted is operator= not handling self assignment correctly. When you do
string s;
s = s;
Then your operator= first deletes the buffer and then copies from it. Just in case you are not convinced that this is an issue (because who would write s=s, thats silly, no?), consider for example a function void foo(string& a, string b&) { a = b; }.
On a more stylistic note, I suggest you to rename the method. inverse suggests that it would return a new string, while invert would make it clear that it modifies the string. (And I have no clue what inconst is supposed to mean.)

can't figure out where the heap overwriting is

#include<iostream>
using namespace std;
class Text{
public:
~Text(){
delete data;
}
char* data{};
int mSize{};
void fill(char* stringInput) {
mSize = strlen(stringInput);
data = new char [mSize];
for (int i = 0; i < mSize; i++){
data[i] = stringInput[i];
}
}
};
class myString{
public:
explicit myString(int size){ // constructor
strAmount = size;
strings = new Text [size];
}
~myString(){ // destructor
delete[] strings;
}
void addString(char* input){
strings[filledAmount].fill(input);
filledAmount++;
}
void delString(int pos){
for ( int i = pos; i < filledAmount; i++){
swap(strings[i], strings[i+1]);
}
strings[filledAmount].data = nullptr;
strings[filledAmount].mSize = 0;
filledAmount--;
}
void eraseEverything(){
for ( int i = 0; i < filledAmount; i++){
strings[i].data = {};
strings[i].mSize = 0;
}
filledAmount = 0;
}
int maxString() const {
int index{};
for ( int i = 0 ; i < filledAmount; i++){
if (strings[i].mSize > strings[index].mSize){
index = i;
}
}
return index;
}
int charAmount(){
int counter{};
for(int i = 0 ; i < filledAmount; i++){
counter+=strings[i].mSize;
}
return counter;
}
double digitPercentage(){
int digitsAmount{};
for(int i = 0; i < filledAmount; i++){
for ( int j = 0; j < strings[i].mSize; j++){
if (isdigit(strings[i].data[j])){
digitsAmount++;
}
}
}
double digitPercent = (digitsAmount/(double)charAmount())*100;
return digitPercent;
}
int filledAmount{};
int strAmount{};
Text* strings;
};
void render_text(myString& obj) {
for (int k = 0; k < obj.filledAmount; k++) {
for (int i = 0; i < obj.strings[k].mSize; i++)
cout << obj.strings[k].data[i];
cout << endl;
}
cout << endl;
}
int main(){
myString a(5);
a.addString((char *) "zxc 1v1 forever shadow fiend");
a.addString((char *) "This is a string");
a.addString((char *) "12345");
a.addString((char *) "Hello");
a.addString((char *) "A1oha Dance");
render_text(a);
a.delString(1);
render_text(a);
int maxInd = a.maxString();
cout << "Max string :\n";
for (int i = 0; i < a.strings[maxInd].mSize; i++) {
cout << a.strings[maxInd].data[i];
}
cout << "\n\n";
}
Please help me find the crash point. I suppose it crashes in the destructor pole, but I still can't figure it out.
This is something like a self-written string class, the problem is that I can't find the place where the problems start.
I also have a thought that the destructor tries to delete too much memory from the heap so the compiler prevents it from doing that. Can I somehow change the size of the strings array?
I have fixed all your memory errors, with the help of address sanitizers
The main change here is:
Add copy constructors and copy assignment operators
Manually delete the last element in the function delString
Though that the code now works, it's not a true modern c++ style code. I highly recommend that your using std::string to replace your Text class. Use std::vector to replace the dynamic array. Then you will stay away from the pain of memory errors. The delString should be replaced with vector::erase,which is much neat than your hand write algorithm.
https://en.cppreference.com/w/cpp/string/basic_string
https://en.cppreference.com/w/cpp/container/vector
I strongly recommend rewriting the code with std::string and std::vector
#include <cstring>
#include <iostream>
using namespace std;
class Text {
public:
~Text() { delete[] data; }
char* data{};
int mSize{};
Text() = default;
Text(const Text& oth) {
mSize = oth.mSize;
data = new char[mSize];
std::copy(oth.data, oth.data + oth.mSize, data);
}
Text& operator=(const Text& oth) {
delete[] data;
mSize = oth.mSize;
data = new char[mSize];
std::copy(oth.data, oth.data + oth.mSize, data);
return *this;
}
void fill(char* stringInput) {
mSize = strlen(stringInput) + 1;
data = new char[mSize];
for (int i = 0; i < mSize; i++) {
data[i] = stringInput[i];
}
}
};
class myString {
public:
explicit myString(int size) { // constructor
strAmount = size;
strings = new Text[size];
}
myString(const myString& oth) {
strAmount = oth.strAmount;
strings = new Text[oth.strAmount];
for (size_t i = 0; i < strAmount; ++i) {
strings[i] = oth.strings[i];
}
}
myString& operator=(const myString& oth) {
delete[] strings;
strAmount = oth.strAmount;
strings = new Text[oth.strAmount];
for (size_t i = 0; i < strAmount; ++i) {
strings[i] = oth.strings[i];
}
return *this;
}
~myString() { // destructor
delete[] strings;
}
void addString(char* input) {
strings[filledAmount].fill(input);
filledAmount++;
}
void delString(int pos) {
for (int i = pos; i < filledAmount; i++) {
swap(strings[i], strings[i + 1]);
}
delete[] strings[filledAmount].data;
strings[filledAmount].data = nullptr;
strings[filledAmount].mSize = 0;
filledAmount--;
}
void eraseEverything() {
for (int i = 0; i < filledAmount; i++) {
strings[i].data = {};
strings[i].mSize = 0;
}
filledAmount = 0;
}
int maxString() const {
int index{};
for (int i = 0; i < filledAmount; i++) {
if (strings[i].mSize > strings[index].mSize) {
index = i;
}
}
return index;
}
int charAmount() {
int counter{};
for (int i = 0; i < filledAmount; i++) {
counter += strings[i].mSize;
}
return counter;
}
double digitPercentage() {
int digitsAmount{};
for (int i = 0; i < filledAmount; i++) {
for (int j = 0; j < strings[i].mSize; j++) {
if (isdigit(strings[i].data[j])) {
digitsAmount++;
}
}
}
double digitPercent = (digitsAmount / (double)charAmount()) * 100;
return digitPercent;
}
int filledAmount{};
int strAmount{};
Text* strings = nullptr;
};
void render_text(myString& obj) {
for (int k = 0; k < obj.filledAmount; k++) {
for (int i = 0; i < obj.strings[k].mSize; i++)
cout << obj.strings[k].data[i];
cout << endl;
}
cout << endl;
}
int main() {
myString a(6);
a.addString((char*)"zxc 1v1 forever shadow fiend");
a.addString((char*)"This is a string");
a.addString((char*)"12345");
a.addString((char*)"Hello");
a.addString((char*)"A1oha Dance");
render_text(a);
a.delString(1);
render_text(a);
int maxInd = a.maxString();
cout << "Max string :\n";
for (int i = 0; i < a.strings[maxInd].mSize; i++) {
cout << a.strings[maxInd].data[i];
}
cout << "\n\n";
return 0;
}

delete[] array caused a breakpoint

I am trying to move a part of my code from main function to the additional void function, but I keep getting a problem with deleting allocated memory in the end. By that moment my program did not printed out my array as it should have. So i am looking for a tip how i can fix this.
#include "pch.h"
#include <iostream>
using namespace std;
void push(char* C, int size, istream &in);
void print_str(char* word, int length);
int main()
{
char* C = new char[0];
int size = 0;
cout << "input your text: ";
push(C, size, cin);
print_str(C, size);
delete[] C;
return 0;
};
void print_str(char* word, int length) {
for (int k = 0; k < length; k++)
{
cout << word[k];
}
cout << " ";
};
void push(char* C, int size, istream& in) {
while (1) {
char current = in.get();
if (current == '\n')
break;
else {
char* text1 = new char[size];
for (int i = 0; i < size; i++)
text1[i] = C[i];
delete[] C;
C = new char[size + 1];
for (int i = 0; i < size; i++)
C[i + 1] = text1[i];
delete[] text1;
C[0] = current;
}
size++;
}
}
Breakpoint
push changes the value of C, so it must return the new value so that the other code can see the change. Like this (for instance)
int main() {
...
C = push(C, size, cin);
...
}
char* push(char* C, int size, istream& in) {
while (1) {
char current = in.get();
if (current == '\n')
break;
else {
char* text1 = new char[size];
for (int i = 0; i < size; i++)
text1[i] = C[i];
delete[] C;
C = new char[size + 1];
for (int i = 0; i < size; i++)
C[i + 1] = text1[i];
delete[] text1;
C[0] = current;
}
size++;
}
return C;
}
you need to define c as pointer to pointer , because changing c in push scope won't change value of c in main
here's the changed code
#include "pch.h"
#include <iostream>
using namespace std;
void push(char** C, int size, istream& in);
void print_str(char* word, int length);
int main()
{
char* C = new char[0];
int size = 0;
cout << "input your text: ";
push(&C, size, cin);
print_str(C, size);
delete[] C;
return 0;
};
void print_str(char* word, int length) {
for (int k = 0; k < length; k++)
{
cout << word[k];
}
cout << " ";
};
void push(char** C, int size, istream& in) {
while (1) {
char current = in.get();
if (current == '\n')
break;
else {
char* text1 = new char[size];
for (int i = 0; i < size; i++)
text1[i] = (*C)[i];
delete[] C;
*C = new char[size + 1];
for (int i = 0; i < size; i++)
C[i + 1] = text1[i];
delete[] text1;
(*C)[0] = current;
}
size++;
}
}
also you can make more efficient algorithms to reach your end ,instead of allocating and deallocating memory for each character witch is costy

String(const char*) constructor memory leak in string class

So I was trying to implement String class and I got an error saying segmentation fault. I think there is memory leak in my constructor. Can you please tell me what I am doing wrong? Thank you.
here is the constructor code and I am not permitted to use any standard library functionality.
String(const char* chars){
int i = 0;
if(chars){
while(chars[i]){
i++;
}
}
len = i;
str = new char[len - 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
};
And also this is my full code:
#include <iostream>
using namespace std;
class String{
public:
char* str;
int len;
String(){
str = new char[0];
len = 0;
};
String(const char* chars){
int i = 0;
if(chars){
while(chars[i]){
i++;
}
}
len = i;
str = new char[len - 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
};
String(const String& s){
if(s.isEmpty() == false){
len = s.len;
str = new char[len];
for(int i = 0; i < len; i++){
str[i] = s.str[i];
}
}
};
~String() noexcept{
if(len > 0)
delete[] str;
};
bool isEmpty() const noexcept{
if(len == 0){
return true;
}
else{
return false;
}
}
unsigned int length() const noexcept{
return len;
}
const char* toChars() const noexcept{
char* temp = new char[len];
int c = 0;
while(temp[c] != '\0'){
temp[c] = str[c];
c++;
}
return temp;
}
};
int main()
{
const char* chars = "Boo is snoring";
String s;
String t{chars};
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
}
The problem with your String(const char* chars) constructor is on this statement:
str = new char[len - 1];
You are allocating less memory then you need, so your for loop is going out of bounds of the allocated memory. You need to allocate at least len number of chars, not len - 1 number of chars:
str = new char[len];
If you intend str to be null-terminated, you need to allocate len + 1 number of chars instead, and then insert a null terminator after the loop is finished:
str = new char[len + 1];
for(int j = 0; j < len; j++){
str[j] = chars[j];
}
str[len] = '\0';
You are making a similar mistake in your toChars() method. You are not null-terminating the output, which is required by the operator<< that you are passing the memory to afterwards:
const char* toChars() const {
char* temp = new char[len + 1];
for(int c = 0; c < len; ++c){
temp[c] = str[c];
}
temp[len] = '\0';
return temp;
}
Note that I removed the noexcept on toChars(). This is because new[] is not noexcept, it can throw a std::bad_alloc exception if it can't allocate memory, which you are not catching. noexcept means the method doesn't throw any exception at all, but that is not the case here.
There are other problems with your code, too:
Your destructor leaks memory if len is 0, ie if your Default constructor is called, or your Converting constructor is called with a null/empty string. If you call new[], you need to call delete[], regardless of the len used.
Your Copy constructor is not initializing str or len if the String being copied is empty.
In main(), you are not delete[]'ing the memory that toChars() returns.
optional for this particular situation, but in general, you are missing a Copy Assignment operator. And, since you are clearly using C++11 or later, you should also add a Move constructor and a Move Assignment operator as well. See the Rule of 3/5/0.
Try this instead (with no additional library functions added, per request):
#include <iostream>
using namespace std;
class String {
public:
char* str = nullptr;
unsigned int len = 0;
String() = default;
String(const char* chars) {
if (chars) {
unsigned int i = 0;
while (chars[i]) {
++i;
}
len = i;
str = new char[len];
for(int j = 0; j < len; ++j) {
str[j] = chars[j];
}
}
}
String(const String& s) {
if (!s.isEmpty()) {
len = s.len;
str = new char[len];
for(int i = 0; i < len; ++i){
str[i] = s.str[i];
}
}
}
~String() noexcept {
delete[] str;
}
String& operator=(const String &s) {
if (&s != this) {
String tmp(s);
char *tmpstr = tmp.str;
unsigned int tmplen = tmp.len;
tmp.str = str;
tmp.len = len;
str = tmpstr;
len = tmplen;
}
return *this;
}
bool isEmpty() const noexcept {
return (len == 0);
}
unsigned int length() const noexcept {
return len;
}
const char* toChars() const {
char* temp = new char[len + 1];
for(unsigned int c = 0; c < len; ++c) {
temp[c] = str[c];
}
temp[len] = '\0';
return temp;
}
};
int main()
{
String t{"Boo is snoring"};
const char *chars = t.toChars();
cout << "t.len : " << t.length() << endl << "toChar() : " << chars << endl;
delete[] chars;
return 0;
}
Although, a simpler way to implement toChars() would look like this instead:
#include <iostream>
using namespace std;
class String {
public:
char* str = nullptr;
unsigned int len = 0;
String() = default;
String(const char* chars) {
if (chars) {
unsigned int i = 0;
while (chars[i]) {
++i;
}
len = i;
str = new char[len + 1];
for(int j = 0; j < len; ++j) {
str[j] = chars[j];
}
str[len] = '\0';
}
}
String(const String& s) {
if (!s.isEmpty()) {
len = s.len;
str = new char[len + 1];
for(int i = 0; i < len; ++i){
str[i] = s.str[i];
}
str[len] = '\0';
}
}
~String() noexcept {
delete[] str;
}
String& operator=(const String &s) {
if (&s != this) {
String tmp(s);
char *tmpstr = tmp.str;
unsigned int tmplen = tmp.len;
tmp.str = str;
tmp.len = len;
str = tmpstr;
len = tmplen;
}
return *this;
}
bool isEmpty() const noexcept {
return (len == 0);
}
unsigned int length() const noexcept {
return len;
}
const char* toChars() const noexcept {
return str ? str : "";
}
};
int main()
{
String t{"Boo is snoring"};
cout << "t.len : " << t.length() << endl << "toChar() : " << t.toChars() << endl;
return 0;
}
You get crash cause you allocate len - 1 chars new char[len - 1], but copy len chars for(int j = 0; j < len; j++) and do not copy zero-char in the end.
The first constructor should be
String(const char* chars) {
len = 0;
if (chars) {
while (chars[len])
len++;
}
str = new char[len + 1];
for (int j = 0; j < len; j++){
str[j] = chars[j];
}
str[len] = 0;
};
I hope you are able to update the second constructor properly.
The destructor should be
~String() noexcept{
delete[] str;
}
I hope you are able to fix other issues.

Segmentation fault on default constructor

When I run my code i get a segmentation fault for calling the default constructor, but not every time I call it. It faults when I try to dynamically allocate new memory after overloading the + operator. I know seg faults happen with misuse of pointers but I call the def. constructor prior in the code and it works.
here is a git repo with main and header if needed:
#include <iostream>
#include "proj1-5-MyString.h"
#include <cmath>
using namespace std;
MyString::MyString() {
capacity = 10;
size = 0;
data = new char[capacity];
data[size] = '\0';
}
//Constructor
MyString::MyString(const char *input) {
this->capacity = 10;
this->size = 0;
this->data = new char[this->capacity];
for (int count = 0; input[count] != '\0'; count++) {
if (count >= this->capacity) {
this->capacity *= 2;
char *temp = new char[this->capacity];
for (int i = 0; i < this->size; i++) {
temp[i] = this->data[i];
}
delete[] this->data;
this->data = temp;
delete[] temp;
}
this->data[count] = input[count];
this->size = count + 1;
}
while ((double) this->size < 0.25 * ((double) (this->capacity))) {
this->capacity = (int)floor(this->capacity / 2);
}
this->data[this->size + 1] = '\0';
}
//Constructor with an initialization character string
MyString::~MyString() {
delete[] this->data;
data = NULL;
}
//Destructor
MyString::MyString(const MyString &that) {
this->data = new char[this->capacity];
this->capacity = that.capacity;
this->size = that.size;
for(int i = 0; i < size; i++){
this->data[i] = that.data[i];
}
while(this->size < 0.25 * (double)(this->capacity)){
this->capacity = (int)((double)that.capacity / 2.0);
}
}
//Copy constructor
MyString &MyString::operator=(const MyString &input) {
if (this->data != input.data) {
delete[] this->data;
this->capacity = input.capacity;
this->size = input.size;
int charCount = 0;
for ( charCount; charCount < input.size; charCount++) {
if (charCount >= this->capacity) {
this->capacity *= 2;
char *temp = new char[this->capacity];
for (int j = 0; j < charCount; j++) {
temp[j] = this->data[j];
}
delete[] this->data;
this->data = temp;
delete[] temp;
}
this->data[charCount] = input.data[charCount];
this->size = charCount + 1;
}
}
return *this;
}
//Overloaded assignment operator, make a copy of MyString object
bool MyString::operator==(const MyString &otherString) const {
int i = 0;
bool same = true;
if (this->size == otherString.size) {
while (i < otherString.size) {
if (this->data[i] == otherString.data[i]) {
same = i <= size;
} else {
same = false;
}
i++;
}
} else {
same = false;
}
return same;
}
//overloaded equivalence relational operator
char &MyString::operator[](int val) {
return this->data[val];
}
//overloaded [ ] should return a char by reference
void MyString::operator+=(const MyString &otherStr) {
*this = *this + otherStr;
}
//overloaded += operator, use to concatenate two MyStrings
MyString MyString::operator+(const MyString &otherStr) const {
//SEGFAULT HERE
MyString doubleString;
cout << doubleString.capacity << endl;
while (doubleString.capacity < (this->size + otherStr.size)) {
doubleString.capacity *= 2;
}
for (int i = 0; i < max(this->size, otherStr.size); i++) {
doubleString.data[i] = this->data[i];
doubleString.data[i + this->size] = otherStr.data[i];
}
doubleString.size = this->size + otherStr.size;
return doubleString;
}
//Create a new MyString object that is the concatenation of two MyString
//objects
void MyString::getline(istream &in, char delimit) {
delimit = '\n';
int size = 10;
char buffer[size];
int charNum = 0;
while (in.get(buffer[charNum++])) {
if (buffer[charNum - 1] == delimit) {
break;
}
if (charNum >= size) {
char *temp = new char[size * 2];
for (int j = 0; j < size; j++) {
temp[j] = data[j];
}
delete[] data;
data = temp;
delete[] temp;
}
}
}
//reads an entire line from a istream. Lines are terminated with delimit
//which is newline ā€˜\nā€™ by default
int MyString::length() const {
return this->size;
}
//return the length of the string
ostream &operator<<(ostream &out, MyString &stringWord) {
for (int i = 0; i < stringWord.size; i++) {
out << stringWord.data[i];
}
return out;
}
//overloaded insertion operator
For instance, if you have this code:
MyString s0 = "ABC";
MyString s1 = "DEF";
auto s2 = s0 + s1;
The first problem is in the constructor of MyString. Your strings are not \0 terminated.
This can be fixed quite easiely though:
MyString::MyString(const char *input)
{
this->capacity = 10;
this->size = 0;
//this->data = new char[this->capacity]; //<- Old
this->data = new char[this->capacity](); //<- New
There are multiple places where you don't initialize the allocated char array. Please check your code and do so. Either via the char() constructor or various other ways (std::fill, memset, ...).
Please remember to debug your own code before posting a question. You can spot this in the debugger pretty easiely.