I'm trying to overloading the operator new of my class, I wanna allocate space for the objects' attributes and for a vector of chars that isn't defined in the class.
But when I instance an new object, the chars of previously object are changing.
Am I doing something wrong?
class StringData
{
public:
friend class String;
int refCount;
int len;
int maxLen;
~StringData()
{
}
StringData()
{
maxLen = 1000;
len = 0;
refCount = 1;
}
StringData(const char * string)
{
maxLen = 1000;
char * data = buffer();
len = 0;
while (string[len] != '\0' && len < (maxLen - 1))
{
data[len] = string[len];
len++;
}
data[len] = '\0';
refCount = 1;
}
char* buffer()
{
return reinterpret_cast<char*>(this + 12);
}
public:
void* operator new(size_t size, int maxLen)
{
return ::operator new(size + round4(maxLen + 1));
}
static int round4(int len)
{
return ((((int) (len / 4)) + 1) * 4);
}
void operator delete(void* obj)
{
::operator delete(obj);
}
void operator delete(void* obj, int size) throw ()
{
::operator delete(obj);
}
};
I use this in other class:
class String{
public:
StringData * data;
String(StringData * data){
this->data = data;
}
public:
String(const char*);
String(const String&);
~String();
String& operator =(const String);
String& operator =(const char *);
int length() const;
bool operator ==(const String&) const;
int compare(const String&) const;
bool operator ==(const char*) const;
int compare(const char*) const;
String& operator +(const String&) const;
String operator +(const char*) const;
String operator +=(const String&);
String operator +=(const char*);
String toLower();
String toUpper();
char operator [ ](int) const;
char& operator [ ](int);
void print() const;
};
String::String(const char * string){
int stringLen = 0;
while (string[stringLen] != '\0')
stringLen++;
data = new (stringLen + 1) StringData(string);
}
String::String(const String& string){
data = string.data;
string.data->refCount++;
}
String::~String(){
this->data->refCount--;
if (this->data->refCount == 0)
delete data;
}
String& String::operator=(const String string){
data->refCount--;
if (this->data->refCount == 0)
delete data;
data = string.data;
string.data->refCount++;
return *this;
}
void String::print() const{
printf("%s\n", data->buffer());
}
And my main function is:
int main(){
String *a = new String("boisahzashdacaraverdeepretaeazuleamarelaecinzaevermelha");
a->print();
String * s = new String("freestepehnoisquevoaashashashhasshashhasssasassadasd");
String * b = new String("kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
a->print();
s->print();
return 0;
}
When I execute, the strings are broken:
boisahzashdacaraverdeepretaeazuleamarelaecinzaevermelha
boisahzashdacaraverd
freestepehnoisquevoa
This is not a great idea:
return reinterpret_cast<char*>(this + 12);
Not only does it fail if you ever add/remove some member functions [or make a virtual function], but it's also dependent on the size of int.
Use
return reinterpret_cast<char*>(this) + sizeof(*this);
[Note newplacement of the end of reinterpret_cast<>()!!]
The first version skips 12 * sizeof(*this) forwards, rather than 12 bytes.
[Of course, on my machine, when I used the 4* multiplier of maxLen, it actually works - it only began failing when I removed that]
This can be simplified:
((((int) (len / 4)) + 1) * 4);
Is this supposed to be 4 times larger than maxlen:
(4 * maxLen)
to:
4 * (len / 4 + 1);
I will be back with edits to this answer once I know the answer to the comment about how it is being tested.
Related
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 am having trouble getting my string class working. My istream function seems to read the data in, and I confirmed that the += was adding the characters, but after the first character, what is added is garbage. I have tried everything I can think of to get it working. Can someone please provide some insight as to what is going wrong. Thanks.
istream& operator >>(istream& ins, string& target)
{
char newInput;
while (ins && isspace(ins.peek()))
ins.ignore();
target = ("");
//int count = 0;
while (ins && !isspace(ins.peek())) //(ins.peek())))
{
ins >> newInput;
target.operator+=(newInput);
}
return ins;
}
void string::operator +=(char addend)
{
if (this->current_length + 1 > this->allocated)
{
reserve(allocated + 1);
}
sequence[current_length] = addend;
current_length += 1;
}
void string::reserve(size_t n)
{
assert(n > current_length);
char *newSequence = new char[n];
if (n == allocated)
{
return;
}
for (size_t i = 0; i < n; i++)
{
newSequence[i] = sequence[i];
}
//destroy old array
delete[] sequence;
//update capacity
allocated = n;
//point data at new array
sequence = newSequence;
}
Full Code: **
#pragma warning(disable : 4996)
#ifndef MAIN_SAVITCH_CHAPTER4_MYSTRING_H
#define MAIN_SAVITCH_CHAPTER4_MYSTRING_H
#include <cstdlib> // Provides size_t
#include <cassert>
#include <string.h>
#include <iostream>
#include <ctype.h>
#include <algorithm>
using namespace std;
namespace main_savitch_4
{
class string
{
public:
// CONSTRUCTORS and DESTRUCTOR
string(const char str[] = "");
string(const string& source);
~string() { delete[] sequence; }
// MODIFICATION MEMBER FUNCTIONS
void operator +=(const string& addend);
void operator +=(const char addend[]);
void operator +=(char addend);
void reserve(size_t n);
void operator =(const string& source);
// CONSTANT MEMBER FUNCTIONS
size_t length() const { return current_length; }
char operator [ ](size_t position) const;
// FRIEND FUNCTIONS
friend ostream& operator <<(ostream& outs, const string& source);
friend bool operator ==(const string& s1, const string& s2);
friend bool operator !=(const string& s1, const string& s2);
friend bool operator >=(const string& s1, const string& s2);
friend bool operator <=(const string& s1, const string& s2);
friend bool operator > (const string& s1, const string& s2);
friend bool operator < (const string& s1, const string& s2);
private:
char *sequence;
size_t allocated;
size_t current_length;
};
// CONSTRUCTOR for the string class:
// string(const char str[ ] = "") -- default argument is the empty string.
// Precondition: str is an ordinary null-terminated string.
// Postcondition: The string contains the sequence of chars from str.
string::string(const char str[])
{
current_length = strlen(str);
allocated = current_length + 1;
sequence = new char[allocated];
for (size_t i = 0; i < allocated; i++)
{
sequence[i] = str[i];
}
}
string::string(const string& source)
//copy constructor
{
current_length = source.current_length;
allocated = current_length + 1;
sequence = new char[allocated];
for (size_t i = 0; i < allocated; i++) {
sequence[i] = source.sequence[i];
}
}
//~string();
// CONSTANT MEMBER FUNCTIONS for the string class:
// size_t length( ) const
// Postcondition: The return value is the number of characters in the
// string.
//
// char operator [ ](size_t position) const
// Precondition: position < length( ).
// Postcondition: The value returned is the character at the specified
// position of the string. A string's positions start from 0 at the start
// of the sequence and go up to length( )-1 at the right end.
char string::operator [ ](size_t position) const
{
assert(position < length());
return (sequence[position]);
}
// MODIFICATION MEMBER FUNCTIONS for the string class:
// void operator +=(const string& addend)
// Postcondition: addend has been catenated to the end of the string.
//
void string::operator =(const string& source)
{
//string assigned to self
if (sequence == source.sequence)
{
return;
}
if (source.current_length > this->allocated)
reserve(source.current_length);
delete[] sequence;
current_length = source.current_length;
allocated = current_length + 1;
for (size_t i = 0; i < allocated; i++) {
sequence[i] = source.sequence[i];
}
}
void string::operator +=(const string& addend)
{
if (this->current_length + addend.current_length > this->allocated)
{
reserve(current_length + addend.allocated);
}
//copy addend to sequence
for (size_t i = 0; i < addend.current_length; i++)
{
sequence[i + current_length] = addend.sequence[i];
}
current_length = current_length + addend.current_length;
}
// void operator +=(const char addend[ ])
// Precondition: addend is an ordinary null-terminated string.
// Postcondition: addend has been catenated to the end of the string.
void string::operator +=(const char addend[])
{
if (this->current_length + strlen(addend) > this->allocated)
reserve(current_length + strlen(addend));
//copy addend to sequence
for (size_t i = 0; i < strlen(addend); i++)
{
sequence[i + current_length] = addend[i];
}
current_length += strlen(addend);
}
// void operator +=(char addend)
// Postcondition: The single character addend has been catenated to the
// end of the string.
//
void string::operator +=(char addend)
{
if ((this->current_length + 1) > (this->allocated))
{
reserve(allocated + 1);
}
//copy addend to sequence
sequence[current_length] = addend;
current_length += 1;
}
// void reserve(size_t n)
// Postcondition: All functions will now work efficiently (without
// allocating new memory) until n characters are in the string.
void string::reserve(size_t n)
{
assert(n > current_length);
//create new array
char *newSequence = new char[n];
if (n == allocated)
{
return;
}
if (n < current_length + 1)
{
n = current_length + 1;
}
for (size_t i = 0; i < current_length; i++)
{
newSequence[i] = sequence[i];
}
delete[] sequence;
//update capacity
allocated = n;
//point data at new array
sequence = newSequence;
}
//Friend Functions
bool operator ==(const string& s1, const string& s2)
{
return (s1 == s2);
}
bool operator !=(const string& s1, const string& s2)
{
return !(s1 == s2); //(strcmp(s1.sequence, s2.sequence) == 0);
}
bool operator >=(const string& s1, const string& s2)
{
return (s1 >= s2); //(strcmp(s1.sequence, s2.sequence) >= 0);
}
bool operator <=(const string& s1, const string& s2)
{
return (s1 <= s2); //(strcmp(s1.sequence, s2.sequence) <= 0);
}
bool operator > (const string& s1, const string& s2)
{
return (s1 > s2); //(strcmp(s1.sequence, s2.sequence) > 0);
}
bool operator < (const string& s1, const string& s2)
{
return (s1 < s2); // (strcmp(s1.sequence, s2.sequence) < 0);
}
// NON-MEMBER FUNCTIONS for the string class
// string operator +(const string& s1, const string& s2)
// Postcondition: The string returned is the catenation of s1 and s2.
//
string operator +(const string& s1, const string& s2)
{
string temp = s1;
temp = s1 + s2;
return temp;
}
// istream& operator >>(istream& ins, string& target)
// Postcondition: A string has been read from the istream ins, and the
// istream ins is then returned by the function. The reading operation
// skips white space (i.e., blanks, newlines, tabs) at the start of ins.
// Then the string is read up to the next white space or the end of the
// file. The white space character that terminates the string has not
// been read.
//
istream& operator >>(istream& ins, string& target)
{
char newInput;
while (ins && isspace(ins.peek()))
ins.ignore();
target = ("");
while (ins && !isspace(ins.peek())) //(ins.peek())))
{
ins >> newInput;
cout << newInput;
target.operator+=(newInput);
}
return ins;
}
// ostream& operator <<(ostream& outs, const string& source)
// Postcondition: The sequence of characters in source has been written
// to outs. The return value is the ostream outs.
ostream& operator <<(ostream& outs, const string& source)
{
outs << source.sequence;
return outs;
}
//
// void getline(istream& ins, string& target, char delimiter)
void getline(istream& ins, string& target, char delimiter)
{
{
int count = 0;
char newLine;
while (ins)
{
ins.get(newLine);
target.operator+=(newLine);
}
}
}
}
#endif
#pragma once
The new[] and delete[] must be balanced.
The string::operator=(const string&) routine is delete[] the sequence array, which later gets delete[] again in the reserve routine.
while (ins && !isspace(ins.peek())) //(ins.peek())))
{
ins >> newInput;
target.operator+=(newInput);
}
Here you:
Check whether the stream has had any errors or attempted to read past the end
Extract the next character, assume this succeeded, then see whether it's a space
Read said character, and again assume this succeeded — if it did, it is assigned to newInput
Insert the contents of newInput to target (with a direct function call for some reason)
If the next read fails, you have no checking of that, and newInput isn't what you think it is.
You've only checked for errors before doing a thing.
Perhaps something like this would better suit:
while (true) {
const auto next = ins.peek();
if (!ins) break;
if (isspace(next)) break;
ins >> newInput;
target += newInput;
}
I'm assuming that if the peek succeeded then the >> will also, but in practice I'd probably put another check for !ins before the target += line, unless I were concerned about speed. In particular, you should double-check whether peek() sets eofbit, because I can't remember and, if it doesn't, you'll definitely need another if.
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;
}
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 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.