For this project we are suppose to cover the topic of dynamic memory allocation and deallocation. The functionality of the 'operator<<; is to output(to terminal or file depending on the type of ostream& os object passed as a parameter to it) the MyString data (the C-string representation held within m_buffer). Right after my program prints to the Terminal "Access Successful." I receive a Segmentation fault (core dumped) I am quite sure that it have something to do with the operator << but I do not know how to fix this. Hence, it fails to print "Testing Operator <<()". Any Suggestions? Thank you!
//int main
int main(){
std::cout << "Testing operator[]()" << std::endl;
MyString ms_access("Access successful (NOT)");
ms_access[17] = 0;
std::cout << "Testing operator<<()" << std::endl;
cout << ms_access << endl;
}
class MyString{
public:
MyString();
MyString(const char * str);
MyString(const MyString & other_myStr);
~MyString();
size_t size() const;
size_t length() const;
const char * c_str() const;
bool operator== (const MyString & other_myStr) const;
MyString & operator= (const MyString & other_myStr);
MyString operator+ (const MyString & other_myStr) const;
char & operator[] (size_t index);
const char & operator[] (size_t index) const;
friend std::ostream & operator<<(std::ostream & os, const MyString & myStr);
private:
void buffer_deallocate();
void buffer_allocate(size_t size);
char * m_buffer;
size_t m_size;
};
//definition
std::ostream& operator<<(std::ostream& os, const MyString& myStr){
//if(os == std::cout){
os << myStr.m_buffer << std::endl;
//}
}
//will allow by-reference accessing of a specific character at index size_tindexwithin the allocated m_bufferchar arrayof a non-constqualified object.
char& MyString::operator[](size_t index){
size_t counter = 0;
while(counter != index){
counter++;
}
return m_buffer[counter];
}
//will allow by-reference accessing of a specific character at index size_tindexwithin the allocated m_bufferchar array of a constqualified object.
const char& MyString::operator[](size_t index)const{
size_t counter = 0;
while(counter != index){
counter++;
}
return m_buffer[counter];
}
void MyString::buffer_deallocate(){
if(m_buffer != NULL){
delete [] m_buffer;
}
}
void MyString::buffer_allocate(size_t size){
if(m_buffer != NULL){
buffer_deallocate();
}
m_size = size;
m_buffer = new char[m_size];
}
MyString::MyString(){
m_size = 0;
m_buffer = NULL;
}
MyString::MyString(const char *str){
m_buffer = NULL;
m_size = strlen(str);
buffer_allocate(m_size);
strcpy(m_buffer,str);
}
MyString::MyString(const MyString &other){
m_buffer = NULL;
m_size = other.m_size;
buffer_allocate(m_size);
strcpy(m_buffer,other.m_buffer);
}
size_t MyString::size()const{
return m_size;
}
size_t MyString::length(){
return strlen(m_buffer)-1;
}
const char* MyString::c_str() const {
char *str = NULL;
str = new char[m_size];
for(size_t i = 0; i < m_size;i++){
str[i] = *(m_buffer+i);
}
return str;
delete [] str;
str = NULL;
}
bool MyString::operator==(const MyString & other)const{
if(strcmp(m_buffer,other.m_buffer) == 0){
return true;
}else if (strcmp(m_buffer,other.m_buffer) != 0){
return false;
}
}
MyString & MyString::operator=(const MyString & str1){
buffer_deallocate();
m_buffer = new char[str1.m_size];
strcpy(m_buffer,str1.m_buffer);
}
MyString MyString::operator+(const MyString & other_myStr)const{
MyString myStr(strcat(m_buffer,other_myStr.m_buffer));
return myStr;
}
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.
[NOTE: My first attempt to implement such class, so please, don't throw stones at me and please don't be rude. Be patient. Thanks.]
So, I have written a custom MyString Class in C++.
Running version can be found here: https://onlinegdb.com/rJMEzP9aD.
I got some strange behaviors. I am trying to get the copy constructors, copy operator and the const char* data() to work together.
EDIT #4: This is strange,
class MyString {
public:
MyString(): pSize{0}, pStr{nullptr} {}
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(cstr, cstr + pSize, pStr);
}
MyString(const MyString& rhs):
pSize{rhs.size()}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
}
~MyString()
{
if(pStr){
delete[] pStr;
}
}
size_t size() const { return pSize; }
size_t capacity() const { return pCapacity; }
const char* data() const { return pStr; }
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
if(pCapacity < rhs.size()){
delete[] pStr;
pSize = rhs.pSize;
pCapacity = (pCapacity < rhs.capacity()) ? rhs.capacity() : pCapacity;
pStr = new char[pCapacity];
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
return *this;
}
pSize = rhs.size();
pCapacity = rhs.capacity();
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
return *this;
}
private:
size_t pSize;
char* pStr;
size_t pCapacity = 14;
inline size_t compute_length(const char* cstr)
{
if(cstr == "")
return 0;
size_t i = 0;
while(cstr[++i] != '\0');
return i;
}
}
Main.cpp,
int main()
{
MyString s("Hello");
//assert(s == "Hello");
cout << s << endl;
s = "";
cout << s << endl;
assert(s == "wow");
return 0;
}
This class supports what a regular String class supports, NOT everything, and obviously far from perfect. However, there's a const char* data() const; which returns a pointer. So, in case the previous class modifies the content of its pointer, the other pointer must also be affected. Like the STD class does. However, for my implementation this comes with some cost. Here I have no idea currently why I have the issues I have. For example: performing s = "something new";, doesn't seem to replace previous. Or, copy/move to previous allocated memory pointer e.g. when sizes of a string and local heap allocated string match vice versa.
compute_length function would return 1 when used on an empty string (try to increment iinside the loop).
Try to write your operators and constructors for char* not only for MyString
The == operators seem not to be complete.
I didn't read the code in detail, so I may have missed important stuff.
Some changes
Always store the string null terminated. It'll help interacting with other APIs.
Two friends added for std::ostream and ==.
Memory cleanup.
Capacity always set to largest allocation seen during the class lifetime.
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
class MyString {
public:
MyString() : MyString(""){}
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{new char[pSize+1]}, pCapacity{pSize}
{
std::copy_n(cstr, pSize + 1, pStr);
}
MyString(const MyString& rhs):
pSize{rhs.size()}, pStr{new char[pSize+1]}, pCapacity{pSize}
{
std::copy_n(rhs.data(), rhs.size()+1, pStr);
}
~MyString()
{
delete[] pStr;
}
size_t size() const noexcept{ return pSize; }
size_t capacity() const noexcept{ return pCapacity; }
const char* data() const noexcept{ return pStr; }
char* data() noexcept { return pStr; }
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
pSize = rhs.size();
if(pCapacity < rhs.size()){
delete[] pStr;
pStr = nullptr;
pStr = new char[pSize+1];
pCapacity = pSize;
}
std::copy_n(rhs.data(), rhs.size() + 1, pStr);
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const MyString& string)
{
os.write(string.pStr, string.size());
return os;
}
friend bool operator==(const MyString& lhs, const MyString& rhs)
{
return std::strcmp(lhs.pStr, rhs.pStr) == 0;
}
private:
size_t pSize{};
char* pStr{};
size_t pCapacity{};
static inline size_t compute_length(const char* cstr)
{
size_t i = 0;
while(cstr[i] != '\0')++i;
return i;
}
};
int main()
{
using std::cout;
using std::endl;
MyString s("Hello");
assert(s == "Hello");
cout << s << endl;
s = "wow";
cout << s << endl;
assert(s == "wow");
return 0;
}
I have a problem with operator>> when I try to use it in my class String. I need to make a string from keyboard, but all my tries make fails. Help me, please.
When I try to turn in this code, I always have a crash.
#include<iostream>
#include<fstream>
using namespace std;
class myString
{
public:
friend std::ostream& operator<< (std::ostream& out, const myString& other);
friend std::istream& operator>> (std::istream& in, myString& other);
myString()
{
str = nullptr;
length = 0;
}
myString(const char* str)
{
length = strlen(str);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = str[i];
}
this->str[length] = '\0';
}
~myString()
{
delete[] this->str;
}
myString(const myString& other)
{
length = strlen(other.str);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = other.str[i];
}
this->str[length] = '\0';
}
myString& operator =(const myString& other)
{
if (this->str != nullptr)
{
delete[] str;
}
length = strlen(other.str);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = other.str[i];
}
this->str[length] = '\0';
return *this;
}
myString& operator =(const char* other)
{
if (this->str != nullptr)
{
delete[] str;
}
length = strlen(other);
this->str = new char[length + 1];
for (int i = 0; i < length; i++)
{
this->str[i] = other[i];
}
this->str[length] = '\0';
return *this;
}
myString& operator +=(myString const& other) {
return (this->operator=(this->operator+(other)));
}
myString operator +(const myString& other)
{
myString newStr;
int thisLength = strlen(this->str);
int otherLength = strlen(other.str);
newStr.length = thisLength + otherLength;
newStr.str = new char[thisLength + otherLength + 1];
int i = 0;
for (; i < thisLength; i++)
{
newStr.str[i] = this->str[i];
}
for (int j = 0; j < otherLength; j++, i++)
{
newStr.str[i] = other.str[j];
}
newStr.str[thisLength + otherLength] = '\0';
return newStr;
}
void Print()
{
cout << str;
}
int Length()
{
return length;
}
bool operator ==(const myString& other)
{
if (this->length != other.length)
{
return false;
}
for (int i = 0; i < this->length; i++)
{
if (this->str[i] != other.str[i])
{
return false;
}
}
return true;
}
bool operator !=(const myString& other)
{
return !(this->operator==(other));
}
char& operator [](int index)
{
return this->str[index];
}
private:
char* str;
int length;
};
ostream& operator<< (ostream& out, const myString& other)
{
out << other.str;
return out;
}
istream& operator >> (istream& in, myString& other)
{
in.getline(other.str, sizeof(other.str));
return in;
}
int main()
{
myString str_3;
cin >> str_3;
cout << str_3;
return 0;
}
sizeof(other.str) is not the size of allocated buffer but the size of the pointer.
other.str is initialized to nullptr in the constructor, so it will try to read into invalid place and it will lead to Segmentation Fault.
Changing the constructor to
myString()
{
str = new char[1];
length = 0;
}
to keep the buffer size always length + 1 and changing the operator>> to
istream& operator >> (istream& in, myString& other)
{
in.getline(other.str, other.length + 1);
return in;
}
will prevent it from getting Segmentation Fault.
This won't solve the problem that the reading will be limited to length of already assigned string.
Improving the behavior will require other modifications like reading characters one-by-one and allocating buffer according to the size read.
You are trying to read to a pointer initialized with nullptr and you use sizeof() of that pointer when you have length data member. Since you already defined assignment operator for char array you can just use it:
istream& operator >> (istream& in, myString& other)
{
char buffer[256];
in.getline(buffer, sizeof(buffer));
other = buffer;
return in;
}
note buffer size is arbitrary here, real application should handle different sizes and grow on need, but that would require more advanced string implementation than yours. For your simplified solution which looks like student task this could be fine.
Note: your operator<< also has issue, if you call it on default constructed object it would lead to UB, as std::ostream does not accept nullptr on const char *. Simple check for nullptr would be sufficient there:
ostream& operator<< (ostream& out, const myString& other)
{
if(other.str)
out << other.str;
return out;
}
Ironically you did check for nullptr for delete[] which is not necessary.
You do not allocate space for your data member str in the default constructor. So when you write
myString str_3;
cin >> str_3;
then your code in operator>> (i.e. in.getline(other.str, sizeof(other.str));) will write to memory that you did not allocate before).
Make sure that str provides enough allocated memory before writing to it, e.g. by using alloc or realloc.
I have tried to write my own String class in C++ using Microsoft Visual Studio 2015. I wrote the class like this;
#include<string.h>
class myString {
private:
char* content;
public:
int size;
myString();
myString(char*);
~myString();
bool operator== (const myString &) const;
bool operator!= (const myString &) const;
myString operator= (const myString &);
myString operator+ (const myString &) const;
myString operator+= (const myString &);
friend std::ostream& operator<< (std::ostream &os, const myString &);
char operator[] (int &) const;
};
std::ostream& operator<<(std::ostream &os, const myString &string) {
os << string.content;
return os;
}
myString::myString() {
size = 0;
content = "\0";
}
myString::myString(char* newContent) {
size = strlen(newContent);
content = new char[size+1];
strcpy(content, newContent);
}
myString::~myString() {
delete[] content;
}
myString myString::operator= (const myString &string) {
if (size != string.size) {
delete[] content;
size = string.size;
content = new char[size+1];
}
strcpy(content, string.content);
return *this;
}
bool myString::operator== (const myString &string) const {
if (size != string.size)
return false;
if (strcmp(content, string.content))
return false;
return true;
}
bool myString::operator!= (const myString &string) const {
if (*this == string)
return false;
return true;
}
myString myString::operator+ (const myString &string) const {
int newSize = size + string.size;
char* newContent = new char[newSize];
strcpy(newContent, content);
strcat(newContent, string.content);
return myString(newContent);
}
myString myString::operator+= (const myString &string) {
*this = *this + string;
return *this;
}
char myString::operator[] (int &index) const {
return content[index];
}
It works fine when I tried to do this;
#include<iostream>
#include "MyString.h"
using namespace std;
int main() {
myString s("my new");
cout << s+" string" << endl;
}
But I am not sure if there is any memory leak in operator+ function in the line char* newContent = new char[newSize]; I am allocating new space from the memory and I need it in the return statement return myString(newContent);.
So I can not deallocate it before this line and I can not deallocate it after the return statement. Am I correct, is there a memory leak? If so, how can I fix this?
EDIT 1 :
I've changed the operator+ function as follows with the help of Prince Dhaliwal;
myString myString::operator+ (const myString &string) const {
myString temp;
int newSize = size + string.size;
char* newContent = new char[newSize + 1];
temp.size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp.content = newContent;
return temp;
}
But since I created the temp locally it calls its destructor before returning it and gives error. I supposed I should allocate memory for temp too. And I changed the function as follows;
myString myString::operator+ (const myString &string) const {
myString* temp= new myString;
int newSize = size + string.size;
char* newContent = new char[newSize+1];
temp->size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp->content = newContent;
return *temp;
}
It works fine now, but I believe there is still memory leak because of the temp variable. If there is a memory leak, how to fix this?
EDIT 2 :
I've fixed it simply by creating a Copy Constructor
There is actually a memory leak in your code. When you are using the + operator in s + " string". In your operator+() definition i.e.
myString myString::operator+ (const myString &string) const {
int newSize = size + string.size;
char* newContent = new char[newSize];
strcpy(newContent, content);
strcat(newContent, string.content);
return myString(newContent);
}
You are allocating the new string here char* newContent = new char[newSize];, copying the older and new part to the new string. And again you are allocating the new string in the constructor return myString(newContent);. But where are you deleting your old string? Its nowhere in your code. So you have to delete the string newContent.
You can do this
myString myString::operator+ (const myString &string) const {
myString temp;
int newSize = size + string.size;
char* newContent = new char[newSize + 1];
temp.size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp.content = newContent;
return temp;
}
UPDATE
You have to create a copy constructor.
myString(const myString &rhs) :
size(rhs.size) {
content = new char[size + 1];
strcpy(content, rhs.content);
}
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.