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.
Related
I've seen some similar questions before asking, but I'm still stuck at the part of concatenating two strings using operator+=.
Currently, I can get separate strings correctly by constructor method. But when I compile code, the line str[length+i] = s[i]; in the method String& String::operator+= (const String& s) shows an error:
no match for ‘operator[]’ (operand types are ‘const String’ and ‘unsigned int’)
So I need your help to fix this bug.
Here's my code:
#include <iostream>
#include <cstring>
class String {
// Initialise char array
char* data;
unsigned length;
public:
// Constructor without arguments
String();
// Constructor with 1 arguments
String(char* s);
// Copy Constructor
String(const String& source);
// Move Constructor
String(String&& source);
// Destructor
~String() { delete[] data; }
/*!
* #brief String length.
* #return Value in String #c length.
*/
unsigned len ( ) const;
/*!
* #brief Append to String.
* #param[in] s A String object.
* #return A String reference to *this.
* #post String will equal the concatenation of itself with #a s.
*/
String& operator+= (const String& s);
};
// Constructor with no arguments
String::String()
: data{ nullptr }
{
data = new char[1];
data[0] = '\0';
}
// Constructor with one arguments
String::String(char* s)
{
if (s == nullptr) {
data = new char[1];
data[0] = '\0';
}
else {
data = new char[strlen(s) + 1];
// Copy character of s[]
// using strcpy
strcpy(data, s);
data[strlen(s)] = '\0';
std::cout << data << "\n";
}
}
// Copy Constructor
String::String(const String& source)
{
data = new char[strlen(source.data) + 1];
strcpy(data, source.data);
data[strlen(source.data)] = '\0';
}
// Move Constructor
String::String(String&& source)
{
data = source.data;
source.data = nullptr;
}
unsigned String::len ( ) const
{
return length;
}
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
for (unsigned j=0; j < length; j++)
str[j] = data[j];
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
delete data;
length = len;
data = str;
return *this;
}
int main()
{
// Constructor with no arguments
String a;
// Convert string literal to
// char array
char temp[] = "Hello world.";
// Constructor with one argument
std::cout << "s1: ";
String s1{ temp };
// Copy constructor
String s11{ a };
char temp1[] = "Goodbye!";
std::cout << "s2: ";
String s2{ temp1 };
String s3 = String s1 + String s2;
return 0;
}
Another way of writing main function:
int main()
{
String s1("Hello World.");
String s2("Goodbye!");
std::cout << "s1: " << s1 << std::endl;
std::cout << "s2: " << s2 << std::endl;
String s3 = s1 + s2;
std::cout << "s3: " << s3 << std::endl;
std::cout << "The last char of s3: " << s3[s3.size()-1] << std::endl;
return 0;
}
Expected result:
s1: Hello World.
s2: Goodbye!
s3: Hello World.Goodbye!
The last char of s3: !
How can I modify my code to get s3 and last char of s3 correctly?
In many of your constructors, you do not set length which leaves it with an indeterminate value - and reading such values makes the program have undefined behavior. So, first fix that:
#include <algorithm> // std::copy_n
// Constructor with no arguments
String::String() : data{new char[1]{'\0'}}, length{0} {}
// Constructor with one argument
String::String(const char* s) { // note: const char*
if (s == nullptr) {
data = new char[1]{'\0'};
length = 0;
} else {
length = std::strlen(s);
data = new char[length + 1];
std::copy_n(s, length + 1, data);
}
}
// Copy Constructor
String::String(const String& source) : data{new char[source.length + 1]},
length{source.length}
{
std::copy_n(source.data, length + 1, data);
}
// Move Constructor
String::String(String&& source) : String() {
std::swap(data, source.data);
std::swap(length, source.length);
}
In operator+= you are trying to use the subscript operator, String::operator[], but you haven't added such an operator so instead of s[i], use s.data[i]:
String& String::operator+=(const String& s) {
unsigned len = length + s.length;
char* str = new char[len + 1];
for (unsigned j = 0; j < length; j++) str[j] = data[j];
for (unsigned i = 0; i < s.length; i++) str[length + i] = s.data[i];
str[len] = '\0';
delete[] data; // note: delete[] - not delete
length = len;
data = str;
return *this;
}
If you want to be able to use the subscript operator on String objects, you would need to add a pair of member functions:
class String {
public:
char& operator[](size_t idx);
char operator[](size_t idx) const;
};
char& String::operator[](size_t idx) { return data[idx]; }
char String::operator[](size_t idx) const { return data[idx]; }
And for String s3 = s1 + s2; to work, you need a free operator+ overload:
String operator+(const String& lhs, const String& rhs) {
String rv(lhs);
rv += rhs;
return rv;
}
Also, to support printing a String like you try in your alternative main function, you need an operator<< overload. Example:
class String {
friend std::ostream& operator<<(std::ostream& os, const String& s) {
os.write(s.data, s.length);
return os;
}
};
Full demo
For starters neither constructor sets the data member length.
So the operator
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
//...
has undefined behavior.
Also provided that the data member length was initialized you need to write
char* str = new char[len + 1];
instead of
char* str = new char[len];
to reserve memory for the terminating zero character '\0' because you are using the standard C string function strcpy in the copy constructor
strcpy(data, source.data);
And the class does not have the subscript operator used in this for loo[
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
And you forgot to append the terminating zero character '\0'.
Pay attention to that there is no member function size in the class used in this expression
s3[s3.size()-1]
And this construction
String s3 = String s1 + String s2;
is invalid. At least you should write
String s3 = s1 + s2;
and correspondingly define the operator +.
I have implemented a class String. When I push_back a String object to a Vector, the program gets stuck. Please help me review it if you're interested.
String.h
#pragma once
#include <iostream>
#include <memory>
#include <string>
class String {
public:
String();
String(const char *);
String(const String &);
String(const std::string &);
String(String &&) noexcept;
String &operator=(String &&) noexcept;
~String();
String &operator=(const String &);
std::string to_string() const;
friend std::ostream &operator<<(std::ostream &os, String s);
private:
std::pair<char *, char *>
alloc_n_copy(char *, char *);
void free();
static std::allocator<char> alloc;
char *beg;
char *end;
size_t length;
};
String.cpp
#include "stdafx.h"
#include <cstring>
#include "String.h"
std::allocator<char> String::alloc = std::allocator<char>();
String::String()
: beg(nullptr), end(nullptr), length(0) {}
String::String(const char *ptr) {
std::cout << "Const char constructor execute" << std::endl;
const char *tmp = ptr;
while (*tmp++ != '\0') ++length;
beg = alloc.allocate(length);
end = std::uninitialized_copy(ptr, tmp, beg);
}
String::String(const std::string &s) {
std::cout << "Const string constructor execute" << std::endl;
strcpy_s(beg, s.size(), s.c_str());
length = s.size();
char *tmp = beg;
end = tmp + length;
}
String::String(const String &s) {
std::cout << "Copy constructor execute" << std::endl;
beg = alloc.allocate(s.length);
end = std::uninitialized_copy(s.beg, s.end, beg);
}
std::pair<char *, char *>
String::alloc_n_copy(char *beg, char *end) {
auto newBeg = alloc.allocate(end - beg);
return{ newBeg, std::uninitialized_copy(beg, end, newBeg) };
}
String &String::operator=(const String &s) {
length = s.length;
auto newStr = alloc_n_copy(s.beg, s.end);
free();
beg = newStr.first;
end = newStr.second;
return *this;
}
String::String(String &&s) noexcept : beg(s.beg), end(s.end), length(s.length) {
std::cout << "Move constructor execute" << std::endl;
s.beg = s.end = nullptr;
s.length = 0;
}
String &String::operator=(String &&s) noexcept {
if (this != &s) {
beg = s.beg;
end = s.end;
length = s.length;
s.beg = s.end = nullptr;
s.length = 0;
}
return *this;
}
void String::free() {
while (length-- >= 0) {
alloc.destroy(end--);
}
alloc.deallocate(beg, length);
}
String::~String() {
free();
}
std::string String::to_string() const {
std::string s(beg);
return s;
}
std::ostream &operator<<(std::ostream &os, String s) {
std::string str(s.beg);
os << str;
return os;
}
main.cpp
int main()
{
vector<String> v;
String s1("abc");
String s2("def");
v.push_back(s1);
v.push_back(s2);
return 0;
}
result:
Const char constructor execute
Const char constructor execute
Copy constructor execute
Move constructor execute
I don't know why the second push_back is a move construction.
And when the push_back finished, the program can't exit. Is there any resource failed to release?
Thanks
The reason that your program blocks is because your destructor never terminates:
void String::free() {
while (length-- >= 0) {
alloc.destroy(--end);
}
alloc.deallocate(beg, length);
}
Since length is an unsigned type, length >= 0 is always true. You probably don't want to be decrementing length here, before it's used as argument to alloc.deallocate(). I suggest:
void String::free() {
while (end > beg) {
alloc.destroy(end--);
}
alloc.deallocate(beg, length);
}
There are other bugs, such as failing to initialise length before using it in the char const* constructor (I don't see why you don't just use std::strlen()) and failing to allocate in the std::string constructor. I recommend using a good set of warnings (I used g++ -std=c++2a -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++) and addressing them all. The problem above was identified very easily this way.
After compiling clear of warnings, then run your code under Valgrind or other memory checker to understand some of the outstanding issues.
I am working on defining my own string class for a homework. It comes to my attention that the following code
class MyString {
public:
MyString(const char* s = NULL) {len = strlen(s); str = new char[len + 1]; strcpy(str, s);}
~MyString() {delete [] str;}
friend ostream& operator << (ostream& ost, const MyString& s) { ost << s.str; return ost;}
friend MyString operator + (const MyString &s1, const MyString &s2) {
int length = strlen(s1.str) + strlen(s2.str);
char *str = new char[length + 1];
strcpy(str, s1.str);
strcat(str, s2.str);
return MyString(str);
}
private:
char * str;
int len;
};
int main () {
MyString s1 = MyString("hello");
MyString s2 = MyString("world");
cout << s1 + s2 << endl;
return 0;
}
works, as the return object is created at the last moment. But the following code
class MyString {
public:
MyString(const char* s = NULL) {len = strlen(s); str = new char[len + 1]; strcpy(str, s);}
~MyString() {delete [] str;}
friend ostream& operator << (ostream& ost, const MyString& s) { ost << s.str; return ost;}
friend MyString operator + (const MyString &s1, const MyString &s2) {
int length = strlen(s1.str) + strlen(s2.str);
MyString temp;
temp.str = new char[length + 1];
strcpy(temp.str, s1.str);
strcat(temp.str, s2.str);
return temp;
}
private:
char * str;
int len;
};
int main () {
MyString s1 = MyString("hello");
MyString s2 = MyString("world");
cout << s1 + s2 << endl;
return 0;
}
Does not, giving me a run time error. So I am confused by why the second approach fails, if a temporary object is defined, modified and returned in the overloaded operator.
The issue is that when you default-construct temp here:
MyString temp;
You have to execute:
MyString(const char* s = NULL) {len = strlen(s); ... }
strlen on a null pointer is undefined. It would work if you instead changed the default argument to:
MyString(const char* s = "")
However, both solutions are still poor in that they both leak memory. In the former, you never delete[] the temporary str. In the latter, your default constructor allocated a new str member, and then you immediately override it with a new allocated str member. The original is leaked.
in your constructor you allocate your memory for your string:
len = strlen(s); str = new char[len + 1]; strcpy(str, s);
so if you append a string later on; there is no memory allocated therefore.
When creating the sum of your two string; you create your "sum-string" like this:
MyString temp;
then the memory allocated for str is unknown; since
len = strlen(NULL);
If you want to continue along; you might consider adding two things:
a check whether s = NULL; then not allocate memory but this will cause trouble later on...
a method to allocate more memory; something like temp.allocate(strlen(s1) + strlen(s2))
The return is creating an object and copying pointer. Then 2 deletes occur on same address
I'm having trouble with dynamic allocation.In my code
am I initializing the dynamic array correctly?.
When I try to write + operator member for my class String, it doesn't return what I want. Any guidance to the correct path would be great.
Ex.
String s1("One");
String s2("Two");
String s3 = s1+ s1;
cout <<s3;
//Output is OneTwo
cout <<s1;
//OUtput is OneTwo.
Also I don't understand why I can not add delete[] buf into my constructor.
class String{
public:
String (const char *s =""):buf(new char[strlen(s)]){
buf = strdup(s);
};
String (const String &s):buf(new char[strlen(s.buf)]){
buf = strdup(s.buf);
delete []buf;
};
String operator =(const String &s){
return buf =strdup(s.buf);
};
char & operator [] (int index){
assert(inBounds(index));
return buf[index];
};
int size()
{
return strlen(buf);
};
String operator + (const String s){
delete []buf;
char *temp = new char[strlen(buf)+strlen(s.buf)];
///NEed to ask about t*his acan get this operator tor work
cout<< s.buf;
return temp;
};
String operator += (const String s){
strcpy(buf + strlen(buf),s.buf);
return buf;
};
void print(ostream & out){
out << buf;
};
void read (istream & in){
in >> buf;
};
~String(){
//delete [] buf;
};
private:
bool inBounds(int x){
return x >= 0 && x < strlen(buf);
};
static int strlen(const char *s){
int len =0;
for(int i=0;s[i] != '\0';i++)
len++;
return len;
};
static char *strcpy(char *dest,const char *src){
int i=0;
for(;(dest[i] = src[i]); ++i);
dest[i] = '\0';
return dest;
};
static char *strdup(const char *s){
char * buf;
buf = new char[strlen(s)+1];
int i=0;
for(;s[i] != '\0';i++)
buf[i] = s[i];
buf[i] = '\0';
return buf;
}
char * buf;
};
Your first constructor
String (const char *s ="") : buf(new char[strlen(s)]){
buf = strdup(s);
}
first allocates a buffer that's one character too small, then it throws it away by pointing bufto the result of strdup – a memory leak.
You want
String (const char *s ="") : buf(new char[strlen(s) + 1]){
strcpy(buf, s);
}
or
String (const char *s ="") : buf(strdup(s))
{
}
Your second constructor
String (const String &s) : buf(new char[strlen(s.buf)]){
buf = strdup(s.buf);
delete []buf;
};
has the same problem with a memory leak, and has the added complication that you immediately deallocate buf.
You want something like
String (const String& s) : buf(strdup(s.buf))
{
}
Your + deallocates buf, allocates an uninitialised (and too small) buffer, prints buf(which is undefined) and then returns a String made from the uninitialised buffer.
The addition operator should not modify *this; it should use += and look like
String operator+ (const String& s) const
{
String result = *this;
result += s;
return result;
};
Which leaves +=, which needs to reallocate buf to be large enough to hold the result.
I'll leave it as an exercise.
And reimplementing standard library functions using the standard name is very confusing.
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.