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.
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 realized string class MyString. Here is code:
#include <iostream>
#include <cstring>
using std::cout;
using std::endl;
class MyString{
private:
char * content;
int length;
void copy(const MyString & source);
public:
MyString();
MyString(const char * source);
~MyString();
MyString(const MyString & source);
void print(void);
MyString & operator = (const MyString &source);
friend std::ostream & operator << (std::ostream & out, const MyString& towrite);
friend std::istream & operator >> (std::istream & in, MyString & toread);
};
MyString::MyString(){
content = new char[1];
content[0] = '\0';
length = 0;
}
MyString::MyString(const char *source){
length = strlen(source);
content = new char[length + 1];
strcpy(content, source);
}
MyString::~MyString(){
delete[] content;
}
void MyString::copy(const MyString & source){
length = source.length;
content = new char[length + 1];
strcpy(content, source.content);
}
MyString::MyString(const MyString & source){
copy(source);
}
void MyString::print(void){
cout << "" << content << endl;
}
MyString &MyString::operator=(const MyString &source){
copy(source);
return *this;
}
std::ostream & operator<<(std::ostream & out,const MyString& towrite){
out << towrite.content;
return out;
}
std::istream & operator >> (std::istream & in, MyString & toread){
int length;
std::cout << "Enter length of word: " << endl;
std::cin >> length;
toread.length = length;
toread.content = new char[toread.length+1];
for (int i = 0; i < toread.length; i++){
in >> toread.content[i] ;
}
toread.content[toread.length] = '\0';
return in;
}
My question is related to overloaded operator >>.
For this main program:
int main(){
MyString word;
std::cout<<"Enter some word: "<<endl;
std::cin>>word;
std::cout<<"Your entered: "<<word<<endl;
}
this is output:
Enter some word:
Enter length of word:
5
stack
Your entered: stack
Process returned 0 (0x0) execution time : 8.313 s
Press any key to continue.
It prints correctly string user entered, but it doesn't "mimic" original string class on the way I want. Here is why.
In case of using C++ string class:
int main(){
std::string word;
std::cout<<"Enter some word: "<<endl;
std::cin>>word;
std::cout<<"Your entered: "<<word<<endl;
}
user doesn't need to enter length of word. Can I achieve this with my class?
EDIT1:
I did it on this way:
std::istream & operator >> (std::istream & in, MyString & toread){
char *temp;
temp = new char[100];
char c;
int i = 0;
while(c != '\n'){
c = getchar();
temp[i++] = c;
}
temp[i] = '\0';
int length = i-1;
toread.length = length;
toread.content = new char[toread.length+1];
for(int i = 0 ; i < toread.length ; i++){
toread.content[i] = temp[i];
}
delete [] temp;
toread.content[toread.length+1]='\0';
}
It works as it should. However, I get warning because I didn't return "in":
||=== Build: Debug in fdsfsdf (compiler: GNU GCC Compiler) ===|
C:\Users\hae\Desktop\fdsfsdf\main.cpp||In function 'std::istream& operator>>(std::istream&, MyString&)':|
C:\Users\hae\Desktop\fdsfsdf\main.cpp|137|warning: no return statement in function returning non-void [-Wreturn-type]|
||=== Build finished: 0 error(s), 1 warning(s) (0 minute(s), 4 second(s)) ===|
||=== Run: Debug in fdsfsdf (compiler: GNU GCC Compiler) ===|
Here's a stripped down version of a similar class I wrote a long time ago. It's an antique, but it should work, and solves some of the issues with your class.
class charray {
public:
charray();
~charray();
charray(const charray&);
charray(const char*);
charray& operator=(const charray&);
charray& operator=(const char*);
void swap(charray&);
const char* c_str() const
{ return m_elem; }
unsigned int size() const
{ return m_size; }
private:
void m_resize(unsigned int size);
char* m_elem;
unsigned int m_size;
};
// private.
void charray::m_resize(unsigned int size)
{
char* elem = new char[size+1];
memcpy(elem, m_elem, std::min(m_size, size));
elem[size] = '\0';
delete [] m_elem;
m_elem = elem;
m_size = size;
}
// public.
charray::charray()
: m_elem(0), m_size(0)
{
m_resize(0);
}
charray::~charray()
{
delete [] m_elem;
}
charray::charray(const charray& src)
: m_elem(0), m_size(0)
{
unsigned int size = src.size();
m_resize(size);
memcpy(m_elem, src.c_str(), size);
}
charray::charray(const char* src)
: m_elem(0), m_size(0)
{
unsigned int size = std::strlen(src);
m_resize(size);
memcpy(m_elem, src, size);
}
charray& charray::operator=(const charray& rhs)
{
charray temp(rhs);
this->swap(temp);
return *this;
}
charray& charray::operator=(const char* rhs)
{
charray temp(rhs);
this->swap(temp);
return *this;
}
void charray::swap(charray& b)
{
std::swap(m_elem, b.m_elem);
std::swap(m_size, b.m_size);
}
Here is what you're probably most interested in. Pay close attention to the details. When dealing with memory directly, the difference between a working implementation and a broken one is often very subtle.
Note: The operators are not friends. They do not access private data.
std::ostream& operator<<(std::ostream& out, const charray& in)
{
return out << in.c_str();
}
std::istream& operator>>(std::istream& in, charray& out)
{
// verify no errors are set, flush tied streams, strip leading
// whitespace.
std::istream::sentry sentry(in);
if (!sentry)
return in;
unsigned int size = 0;
unsigned int tail = 0;
char* temp = 0;
int next; // #note int not char (to record EOF).
while ((next = in.get()) != in.eof() && !std::isspace(next)) {
// if temp buffer is exhausted, then double the buffer size.
// (base size of 16).
if (tail == size) {
unsigned int newsize = std::max(2*size, 16u);
char* newtemp = new char[newsize+1];
memcpy(newtemp, temp, size);
delete [] temp;
temp = newtemp;
size = newsize;
}
temp[tail++] = next;
}
// #note because the stream is prepeared with istream::sentry, there
// will be at least one non-whitespace character in the stream.
assert(temp != 0);
temp[tail] = '\0';
out = temp;
delete [] temp;
return in;
}
A much easier and safer way to do the exact same thing,
#include <vector>
std::istream& operator>>(std::istream& in, charray& out)
{
std::istream::sentry sentry(in);
if (!sentry)
return in;
std::vector<char> temp;
int next;
while ((next = in.get()) != in.eof() && !std::isspace(next))
temp.push_back(next);
temp.push_back('\0');
out = &temp[0];
return in;
}
Edit
The above is outmoded (pre C++11). A modern implementation would likely handle construction and assignment differently. Here are updated versions of those methods,
Note: The method m_resize is gone. Everything is handled through constructors.
charray::charray(const char* src, unsigned int size)
: m_elem{ new char[size+1]{} }, m_size{ size }
{
std::copy(src, src + size, m_elem);
}
charray::charray()
: charray(nullptr, 0)
{}
charray::charray(const charray& src)
: charray(src.m_elem, src.m_size)
{}
charray::charray(const char* src)
: charray(src, std::strlen(src))
{}
charray::charray(charray&& src)
: m_elem{ src.m_elem }, m_size{ src.m_size }
{
src.m_elem = nullptr;
src.m_size = 0;
}
// handle both move and copy assignment.
charray& charray::operator=(charray rhs)
{
this->swap(rhs);
return *this;
}
Hope this helps. Good luck.
I have a problem with my custom string operator += so that if I wanted to add a const char* to my custom string String, then it wouldn't add it, it would just be the String. I can get it to work with other custom String objects but not with c strings.
code in String.cpp
String::String(){
length =0;
capacity = 1024;
myStr = new char[0];
}
String::String (const String& s)
{
length = s.getLength();
myStr = new char[length];
for (unsigned j=0; j < length; j++){
myStr[j] = s[j];
}
}
String::String(char c){
length = 1;
myStr = new char(c);
}
String::String(const char* val){
if(val!= nullptr){
int counter = 0;
while(val[counter]!='\0')counter++; //Find length of the char array
length= counter;
myStr = new char[counter];
for(int i =0;i<counter;i++){
myStr[i]=val[i];
}
}else{
length = 1;
myStr = new char[length];
myStr[0] = '\0';
}
}
ostream& operator<< (std::ostream& os, const String& s)
{
os<<s.myStr;
return os;
}
istream& operator>> (std::istream& is, String& s)
{
char* c = new char[1000];
is >> c;
s = String(c);
delete[] c;
return is;
}
String::~String(){
delete[] myStr;
}
int String::getIndex(char getInd) const {
for(int i = 0;i<length;i++){
if(myStr[i]==getInd){
return i;
}
}
}
unsigned String::getLength() const{
return length;
}
String& String::operator+= (const String& s){
unsigned len = length+s.getLength();
char* str=new char[len];
for(int i = 0;i<length;i++){
str[i]=myStr[i];
}
for(int i = 0;i<s.length;i++){
str[length+1]=s[i];
}
delete myStr;
length = len;
myStr= str;
return *this;
}
String& String::operator+=(const char *c) {
unsigned l =0;
while(c[l] !='\0'){
l++;
}
unsigned len =length+l;//new length of new string
char* str = new char[len];
for(int i = 0;i<getLength();i++) {
str[i] = myStr[i];
}
for(int j = 0; j<l;j++){
str[len+j]=c[j];
}
delete myStr;
length = len;
myStr = str;
return *this;
}
String& String::operator=(const String& s){
if(this == &s){
return *this;
}
delete myStr;
length = s.getLength();
myStr = new char[length];
for(int i = 0;i<length;i++){
myStr[i]=s[i];
}
}
char& String::operator[](unsigned i){
if(i>=length) throw 1;
return myStr[i];
}
char String::operator[] (unsigned j) const
{
if (j >= length) throw 1;
return myStr[j];
}
Code in main.cpp
int main() {
char c[] = {'h','i'}
String add = "iscool";
String str="strworks";
str+=add;
str+=c;
cout<<str<<endl;
return 0;
}
The outcome for this program right now in the console is:
strworks
I am not sure what to change about the const char* c += operator function, i believe this is where the error is. I am also trying to use this string in a getline function but I try getline(file,String& s, ','); there is an error and I would appreciate any tips on how to implement the getline function with my new String class. The String str should be histrworks in the second line of output, however it is blank. I have already re written the logic of the += operator but it doesn't seem to be working. Let me know if there is any more information I need to give out that could assist you guys in finding the problem. The ultimate goal of this program is to read in a file of movie reviews and train an algorithim to find out if the review is positive/negative.
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
Memory Management : character arrays and = operator
Q. In terms of Memory Management, What error would you have with the following code?
class String
{
public:
String(const char right[]);
String& operator= (const String& right);
int length() const;
private:
char* buffer;
int len;
};
int String::length() const {return len;}
String::String(const char right[])
{
len = 0;
while (right[len] != '\0')
len++;
buffer = new char[len+1];
for (int i = 0; i < len; i++)
buffer[i] = right[i];
buffer[len] = '\0';
}
String& String::operator= (const String& right)
{
int n = right.length();
for (int i = 0; i <= n; i++)
buffer[i] = right.buffer[i];
return *this;
}
Answer.
I have no clue... Could you help me?
The array size seems okay... new operator...
is it because of the dangling pointer because there is no delete operator?
Or rule of three?
I would appreciate your help!
Please let me know.
Thanks,
Is all I can suggest
~String()
{
if(buffer)
delete [] buffer;
len = 0;
}
String(int length)
{
buffer = new char[length];
len = length;
}
String String::operator = (String rhs)
{
if(this != &rhs)
{
delete [] buffer;
buffer = new char[strlen(rhs.m_str) + 1];
strcpy(buffer, rhs.buffer);
}
return *this;
}
If you don't have a destructor, every time you new the buffer in your class, it will be a memory leak because your code has no way of getting rid of it.