Does the assignment code will lead to crash? - c++

class SMTkrSym {
enum { SMTKRLEN = 16 };
uint8 data[SMTKRLEN];
public:
SMTkrSym() { memset(&data, 0, sizeof data); }
SMTkrSym(const char* pIn) { if (Read(pIn) < 0) memset(&data, 0, sizeof data); }
int16 Show(char* outstr) const; // display ticker symbol
int16 Read(const char* instr); // read ticker symbol
bool operator==(const SMTkrSym& rhs) const { return strncmp((char*)data, (char*)rhs.data, SMTKRLEN) == 0; }
bool operator!=(const SMTkrSym& rhs) const { return !(*this == rhs); }
bool operator>(const SMTkrSym& rhs) const { return (strncmp((char*)data, (char*)rhs.data, SMTKRLEN) > 0); }
bool operator<=(const SMTkrSym& rhs) const { return !(*this > rhs); }
bool operator<(const SMTkrSym& rhs) const { return (strncmp((char*)data, (char*)rhs.data, SMTKRLEN) < 0); }
bool operator>=(const SMTkrSym& rhs) const { return !(*this < rhs); }
};
unsigned int SMTkrSym::Read(const char* instr)
{
unsigned int i,j;
for (i = 0; (i < SMTKRLEN) && *instr; i++) // display until null
data[i] = *instr++;
for (j = i; j < SMTKRLEN; j++)
data[j] = '\0'; // null out rest of symbol
return i; // return actual number of characters
}
Usage of this class as below:
char pData[] = "helloworldyyyyyy";
char* p = pData;
SMTkrSym key1(pīŧ‰;
SMTkrSym key2;
key2 = key1;
The length of pData is equal to 16.
The SMTkrSym copy constructor will call read function and it will not put '\0' terminator character when the length of instr is larger or equal to 16.
My question is when program run key2 = key1 and does it will lead to crash?
When run key2 = key1 and it will call default assignment operator function.
But key1's data member variable doesn't include terminator character.
My concern is that if default assignment function uses strcpy function to
copy data member variable and it may lead to memory over flow.
I think if assignment function just look like memcpy(&key2, &key1,
sizeof(key1)), it won't crash. How does default assignment works?

The default compiler-provided operator= won't use strcpy() to copy data[]... it will safely copy all characters in the array, as if by memcpy as you say, though it may not actually use memcpy.

Related

What's wrong, why is the "copy assignment operator" invoked right after "copy constructor"?

[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;
}

Overloading operator>> C++

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.

To clarify concept of relational operator overloading (Debugging help needed)

Let me tell you guys I am beginner to c++.
For educational and learning purpose I have created my own string class named MyString. As per instructions from my instructor I am not allowed to use standard library functions to compare two strings.
MyString class contains char type pointer and integer type variable that holds length of string i.e:
class MyString{
char *str; int len;
public:
MyString(){
len = 1;
str = new char[len];
str[len - 1] = '\0';
}
MyString(char *p){
int count = 0;
for (int i = 0; p[i] != '\0'; i++){
count++;
}
len = count;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = p[i];
}
}
int length(){
return len;
}
bool operator < (MyString obj){
char temp;
if (len < obj.len){ return true; }
if (len>obj.len){ return false; }
if (this->len == obj.len){
for (int i = 0; i < len; i++){
if (this->str[i] < obj.str[i])
{
return true;
}
}
}
}
bool operator > (MyString obj) {
if (len > obj.len) {
return true;
}
if (len<obj.len) {
return false;
}
if (this->len == obj.len)
{
for (int i = 0; i < this->len; i++) {
if (this->str[i] > obj.str[i]) {
return true;
}
}
}
}
bool operator == (MyString obj) {
int count = 0;
if (this->len == obj.len){
for (int i = 0; i < this->len; i++) {
if (this->str[i] == obj.str[i]) {
count++;
}
}
if (count == len) {
return true;
}
}
}
char & operator[](int i) {
return str[i];
}
};
Here is main
int main()
{
char arr1[30], arr2[30];
cout << "Enter first MyString: ";
cin.get(arr1, 30);
cin.ignore();
cout << "Enter second MyString: ";
cin.get(arr2, 30);
MyString s1(arr1); //parametrized constructor
MyString s2(arr2);
cout << "Length of s1:" << s1.length() << endl;
cout << "Length of s2:" << s2.length() << endl;
if (s1<s2) // < operator overloaded
cout << "s1 < s2" << endl;
else if (s1>s2) // > operator overloaded
cout << "s1 > s2" << endl;
else if (s1 == s2) // == operator overloaded
cout << "s1 == s2" << endl;
return 0;
}
My algo for comparing two strings is:
i).First check length of two strings if len(length of s1) is less than obj.len(length of s2) than it returns true.
ii).if lengths are equal, compare for each element of s1 char array with s2 char array.Even if one of element of s1 char array is less than that s2 char array element (in ASCII) than return true otherwise return false.
The problems is whenever program executes, on console it shows "s1< s2" no matter if two strings passed are equal .
You're trying to write simple classes that allocate resources. This is a very important skill to have. What you've written so far has some good code, but also a lot of mistakes. The main errors are
Wrong algorithm for operator<. In your code "hello" < "goodbye" which is incorrect
Missing return statements.
Lack of destructor, so your class leaks memory.
Once you add a destructor you will also need a copy constructor and copy assignment operator otherwise your code will crash by freeing the same memory twice, this is known as the rule of three. Google it as it's perhaps the most important piece of C++ advice you'll read.
Lack of awareness of const correctness.
Lack of awareness of pass by reference.
Less than ideal signatures for your overloaded operators.
Missing some important methods on your class
Missing a few implementation tricks.
Putting all that together here an implementation of your class following the rules you've been given, with a few comments as well
class MyString {
char *str; int len;
public:
// default constructor should create a empty string, i.e. a zero length string
MyString() {
len = 0;
str = new char[len];
}
// contents of p are not changed so make it const
MyString(const char *p) {
int count = 0;
for (int i = 0; p[i] != '\0'; i++){
count++;
}
len = count;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = p[i];
}
}
// destructor, frees memory
~MyString() {
delete[] str;
}
// copy constructor, similar to the above except it starts from a MyString
MyString(const MyString& o) {
len = o.len;
str = new char[len];
for (int i = 0; i < len; i++){
str[i] = o.str[i];
}
}
// swap method, efficient exchange of two strings
void swap(MyString& o)
{
int t1 = o.len;
o.len = len;
len = t1;
char* t2 = o.str;
o.str = str;
str = t2;
}
// assignment operator, uses copy and swap idiom
MyString& operator=(MyString o) {
swap(o);
return *this;
}
// length does not modify the string, so it should be decalred const
int length() const {
return len;
}
char& operator[](int i) {
return str[i];
}
// need a const version of operator[] as well, otherwise you won't be able to do [] on a const string
char operator[](int i) const {
return str[i];
}
};
// operator< should be a function not a class method. This is the only way to get
// C++ to treat the two arguments symmetrically. For instance with your version
// "abc" < str is not legal, but str < "abc" is. This oddity is because C++ will
// not implicitly create a MyString object to call a MyString method but it will implicitly
// create a MyString object to pass a parameter. So if operator< is a function you will
// get implicit creation of MyString objects on either side and both "abc" < str and
// str < "abc" are legal.
// You also should pass to parameters by const reference to avoid unnecessary
// copying of MyString objects.
// Finally this uses the conventional algorithm for operator<
bool operator<(const MyString& lhs, const MyString& rhs) {
for (int i = 0; ; ++i)
{
if (i == rhs.length())
return false;
if (i == lhs.length())
return true;
if (lhs[i] > rhs[i])
return false;
if (lhs[i] < rhs[i])
return true;
}
}
// This is the easy way to write operator>
bool operator>(const MyString& lhs, const MyString& rhs) {
return rhs < lhs;
}
// This is the easy way to write operator<=
bool operator<=(const MyString& lhs, const MyString& rhs) {
return !(rhs < lhs);
}
// This is the easy way to write operator>=
bool operator>=(const MyString& lhs, const MyString& rhs) {
return !(lhs < rhs);
}
// operator== is a function not a method for exactly the same reasons as operator<
bool operator==(const MyString& lhs, const MyString& rhs) {
if (lhs.length() != rhs.length())
return false;
for (int i = 0; i < lhs.length(); ++i)
if (lhs[i] != rhs[i])
return false;
return true;
}
// this is the easy way to write operator!=
bool operator!=(const MyString& lhs, const MyString& rhs) {
return !(lhs == rhs);
}
There are countless issues with your code, so I'll provide an improved, commented implementation for you:
class MyString
{
char* str;
unsigned int len; // strings can't have negative length; using unsigned reflects this better
// even better: use size_t; this is the type for the concrete system
// able to cover any allocatable memory size
public:
MyString()
: str(new char[1]), len(1) // prefer initializer list
{
str[0] = 0; // does not matter if you use 0 or '\0', just my personal preference...
}
MyString(char const* p)
// you make a copy of, so have a const pointer (you can pass both const and non-const to)
{
for(len = 1; p[len] != 0; ++len);
// ^ make sure to copy the terminating null character as well!
str = new char[len];
for (unsigned int i = 0; i < len; i++)
{
str[i] = p[i];
}
// or use memcpy, if allowed
}
// OK, above, you allocated memory, so you need to free it again:
~MyString() // if you want to be able to inherit from, it should be virtual;
// strings, though, most likely should not be inherited from...
{
delete[] str;
}
// C++ creates a default copy constructor; this one, however, just copies all members by value
// i. e. copies the POINTER str, but not the memory pointed to, i. e. does not perform a deep copy
// which is what you need, however, to avoid double deletion:
MyString(MyString const& other)
: str(new char[other.len]), len(other.len)
{
for (unsigned int i = 0; i < len; i++)
{
str[i] = other.str[i];
}
}
// similar for assignment; I'm using copy and swap idiom to reduce code duplication here:
MyString& operator=(MyString other)
{
swap(other);
return *this;
}
void swap(MyString& other)
{
char* str = this->str;
unsigned int len = this->len;
this->str = other.str;
this->len = other.len;
other.str = str;
other.len = len;
}
unsigned int length() const
// ^^^^^ allows to retrieve length from a
// const MyString as well!
{
return len;
}
// fine, you can change the character within the string
char& operator[](unsigned int i)
{
return str[i];
}
// but what, if you have a const MyString???
// solution:
char operator[](unsigned int i) const
// ^^^^^
{
return str[i];
}
// you could alternatively return a const reference,
// but char is just too small that a reference would be worth the effort
// additionally: a reference could have the const casted away by user
// which is not possible by returning a copy, so we gain a little of safety as well...
bool operator<(MyString const& other) const
// ^^^^^^
// we don't need a copy and don't want a copy(it would just costs runtime and memory for nothing)!
// -> pass by const reference
// additionally, we want to be able to do comparison on const this as well (see length)
//
{
// have you noticed that you have one and the same code in all of your comparison operators???
// only the comparison itself changes lets have it just a little bit cleverer:
return compare(other) < 0;
}
bool operator>(MyString const& other) const
{
return compare(other) > 0;
}
bool operator==(MyString const& other) const
{
return compare(other) == 0;
}
// and for completeness:
bool operator<=(MyString const& other) const
{
return compare(other) <= 0;
}
bool operator>=(MyString const& other) const
{
return compare(other) >= 0;
}
bool operator!=(MyString const& other) const
{
return compare(other) != 0;
}
// the upcoming space ship operator (<=>) will simplify this, well, OK, but for now, we don't have it yet...
int compare(MyString const& other) const
{
// I decided to compare "abcd" smaller than "xyz" intentionally
// for demonstration purposes; just place your length checks
// back to get your original comparison again
unsigned int pos = 0;
// EDIT: "stealing" john's implementation, as superior to
// mine (with minor adaptions) ...
for (unsigned int pos = 0; ; ++pos)
{
///////////////////////////////////////////////////
// if you have your original length checks placed back above,
// just have the following check instead of the active one:
// if(pos == len) return 0;
if (pos == len)
{
return pos == other.len ? 0 : -pos - 1;
}
if (pos == other.len)
{
return pos + 1;
}
///////////////////////////////////////////////////
if(str[pos] < other.str[pos])
{
return -pos - 1;
}
if(str[pos] > other.str[pos])
{
return pos + 1;
}
}
return 0;
}
// WARNING: above code has yet an issue! I wanted to allow (for demonstration)
// to return positional information so that we not only see the result of comparison
// but can conclude to at WHERE the two strings differ (but need 1-based offset for to
// distinguish from equality, thus addition/subtraction of 1);
// however, on VERY large strings (longer than std::numeric_limits<int>::max()/-[...]::min()), we get
// signed integer overflow on the implicit cast, which is undefined behaviour
// you might want to check against the limits and in case of overflow, just return the limits
// (leaving this to you...)
// alternative: just return -1, 0, +1, the issue is gone as well...
};
OK, you now could just copy this code, strip the comments and present it as "your" solution. This is not what I intended this answer for! Take your time and read my comments carefully – you can learn quite a bit from...
Finally: there is yet another improvement possible: Before C++11, you could only copy data, if passing objects by value. Since C++, we additionally can move data from one object into another one – however, the type needs to support move semantics. You can do so by additionally providing a move constructor and copy assignment:
MyString(MyString&& other)
: str(nullptr), len(0)
{
// delete[]'ing nullptr (in other!) is OK, so we don't need
// to add a check to destructor and just can swap again...
swap(other);
}
MyString& operator=(MyString&& other)
{
// and AGAIN, we just can swap;
// whatever this contained, other will clean it up...
swap(other);
return *this;
}
You might be interested in further reading:
rule of three
rule of five
copy and swap idiom
In your function,
bool operator < (MyString obj){
I see no way to return false at the end!
It only returns true if it reach the 3rd if.
In addition, as others mentioned, the length does not imply the comparison in the way that you implemented.
Just a comment: Your code is prone to memory leak. It allocates but does not release the memory.
This code has lots of mistakes:
No copy constructor and copy is created
Missing destructor saves the day (memory leak, but thanks to that you do not have a crash for point 1)
len = 1 for empty string (default constructor).
MyString(char *p) do not add terminating character
not using const MyString &obj (unneeded copies).
missing return values at the end of various branches of methods
bool operator < (const MyString &obj) {
if (len < obj.len) {
return true;
}
if (len>obj.len) {
return false;
}
for (int i = 0; i < len; i++) {
if (this->str[i] != obj.str[i]) {
return this->str[i] < obj.str[i];
}
}
return false;
}

String class istream issues

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.

Logical Comparison == operator overload

I need to do some logical comparison and return a boolean answer.
Here is the code from the .cpp file:
bool MyString::operator==(const MyString& other)const
{
if(other.Size == this.Size)
{
for(int i = 0; i < this.Size+1; i++)
{
if(this[i] == other[i])
return true;
}
}
else
return false;
}
Here is what is called from main.cpp file:
if (String1 == String4)
{
String3.Print ();
}
else
{
String4.Print ();
}
Here are there compiling errors I get:
error: request for member `Size` in `this`, which is of non-class type `const MyString* const`
error: no match for `operator[]` in `other[i]`
this is a pointer, hence you have to dereference it:
this->Size;
Also I think that logic of your operator== is flawed - here, it returns true if any of characters is equal to character on same position in second string. Change your loop to
for(int i = 0; i < this->Size+1; i++)
{
if(this[i] != other[i])
return false;
}
and put return true; instead of last part of your code (else clause) to compare entire strings.
As Seth mentioned, you can't use operator[] on this as above - this way it's treated as array (i.e. this[i] is really *(this + i) - so not what's you are thinking it is). Access your internal storage member instead.
Problems with your code:
this[i]: You apparently want to access the ith character of the string here. This isn't doing that. Assuming your class overloads operator[], you want (*this)[i]. Alternatively, you could directly access the internal representation of the string.
if(this[i] == other[i]) return true;: Think about what this means with respect to comparing the strings "A1" and "AB".
for () {...}: What happens when you exit the loop? You need to return something if the comparisons manage to make it through the loop without returning.
You haven't specified if you can use the C++ standard algorithms or not.
Here you have illustrated both versions, using hand-written loop and std::equal algorithm:
//#define USE_STD_ALGORITHM 1 // uncomment to enable std::equal version
#include <cassert>
#include <algorithm>
#include <stdexcept>
// NOTE: partial simplest definition for the test and presentation purposes only.
struct MyString
{
MyString(char const* s, std::size_t size) : data(s), Size(size) {}
char const& operator[](std::size_t index) const;
bool operator==(const MyString& other) const;
private:
char const* data;
std::size_t Size;
};
char const& MyString::operator[](std::size_t index) const
{
if (index < Size)
return data[index];
throw std::out_of_range("index invalid");
}
bool MyString::operator==(const MyString& other) const
{
if (this->Size == other.Size)
{
#ifdef USE_STD_ALGORITHM
return std::equal(data, data+Size, other.data);
#else
bool equal = true;
for(std::size_t i = 0; i < this->Size; ++i)
{
if((*this)[i] != other[i])
{
equal = false;
break;
}
}
return equal;
#endif
}
return false;
}
int main()
{
char const* a = "abc";
char const* b = "abc";
MyString sa(a, 3);
MyString sb(b, 3);
assert(sa == sb);
char const* c = "adc";
MyString sc(c, 3);
assert(!(sa == sc));
char const* d = "ab";
MyString sd(d, 2);
assert(!(sa == sd));
}
Good luck!