how to overload accessor and mutator operator[] in c++ - c++

I want to write a String class. And want to use subscript to access element in my String. So, I write two member function, one to get the element in the String, and another to set the element in the String. Please look at following code;
#include <iostream>
#include <algorithm>
using namespace std;
class String {
public:
String();
String(const char *s);
char &operator[] (int index);
char operator[] (int index) const;
private:
char *arr;
int len;
};
String::String() {
arr = new char[1];
arr[0] = '\0';
len = 0;
}
String::String(const char *s) {
len = strlen(s);
arr = new char[len + 1];
std::copy(s, s + len + 1, arr);
}
//mutator operator[] ---> used to change data members;
char& String::operator[](int index)
{
cout << "mutator []" << endl;
if (index > len || index < 0)
throw std::out_of_range("Index out of range");
return arr[index];
}
//Accessor operator[]---> used to read data members
char String::operator[](int index) const
{
cout << "accessor []" << endl;
if (index > len || index < 0)
throw std::out_of_range("Index out of range");
return arr[index];
}
int main()
{
String s1 = "abc";
s1[1] = 'b'; //---> should use mutator operator
String s2 = "efg";
s2[1] = s1[2]; //---> should use both accessor and mutator operator
char a = s1[2]; //---> should use accessor operator
cout << s2[1] << endl; //---> should use accessor operator
}
when I run this code. It's outputs are all mutator; It confuses me a lot;

Let's see this case with the point of view of the compiler. I present you this code:
String s2;
/* things */ s1[2] /* things */
What function do you choose? The accessor or the mutator? Since s2 is not a const object, let's take the non-const version!
That's why your code is always printing mutator, the compiler will not choose which function to call depending of what you do with the result. Whether you call the char's assignment operator or not.
And your const version should not return a copy, but a const reference:
char& operator[](size_t index);
const char& operator[](size_t index) const;
You will get a compilation error instead of a value not assigned if you try to write into a const string.

char operator[] (int index) const; will only be called when you have a const String. If we change your main() to:
int main()
{
const String s1 = "abc";
char a = s1[2]; //---> should use accessor operator
}
It will ouptut:
accessor []
Live Example

Related

Concatenate 2 string using operator+= in class C++

I've seen some similar questions before asking, but I'm still stuck at the part of concatenating two strings using operator+=.
Currently, I can get separate strings correctly by constructor method. But when I compile code, the line str[length+i] = s[i]; in the method String& String::operator+= (const String& s) shows an error:
no match for ‘operator[]’ (operand types are ‘const String’ and ‘unsigned int’)
So I need your help to fix this bug.
Here's my code:
#include <iostream>
#include <cstring>
class String {
// Initialise char array
char* data;
unsigned length;
public:
// Constructor without arguments
String();
// Constructor with 1 arguments
String(char* s);
// Copy Constructor
String(const String& source);
// Move Constructor
String(String&& source);
// Destructor
~String() { delete[] data; }
/*!
* #brief String length.
* #return Value in String #c length.
*/
unsigned len ( ) const;
/*!
* #brief Append to String.
* #param[in] s A String object.
* #return A String reference to *this.
* #post String will equal the concatenation of itself with #a s.
*/
String& operator+= (const String& s);
};
// Constructor with no arguments
String::String()
: data{ nullptr }
{
data = new char[1];
data[0] = '\0';
}
// Constructor with one arguments
String::String(char* s)
{
if (s == nullptr) {
data = new char[1];
data[0] = '\0';
}
else {
data = new char[strlen(s) + 1];
// Copy character of s[]
// using strcpy
strcpy(data, s);
data[strlen(s)] = '\0';
std::cout << data << "\n";
}
}
// Copy Constructor
String::String(const String& source)
{
data = new char[strlen(source.data) + 1];
strcpy(data, source.data);
data[strlen(source.data)] = '\0';
}
// Move Constructor
String::String(String&& source)
{
data = source.data;
source.data = nullptr;
}
unsigned String::len ( ) const
{
return length;
}
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
for (unsigned j=0; j < length; j++)
str[j] = data[j];
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
delete data;
length = len;
data = str;
return *this;
}
int main()
{
// Constructor with no arguments
String a;
// Convert string literal to
// char array
char temp[] = "Hello world.";
// Constructor with one argument
std::cout << "s1: ";
String s1{ temp };
// Copy constructor
String s11{ a };
char temp1[] = "Goodbye!";
std::cout << "s2: ";
String s2{ temp1 };
String s3 = String s1 + String s2;
return 0;
}
Another way of writing main function:
int main()
{
String s1("Hello World.");
String s2("Goodbye!");
std::cout << "s1: " << s1 << std::endl;
std::cout << "s2: " << s2 << std::endl;
String s3 = s1 + s2;
std::cout << "s3: " << s3 << std::endl;
std::cout << "The last char of s3: " << s3[s3.size()-1] << std::endl;
return 0;
}
Expected result:
s1: Hello World.
s2: Goodbye!
s3: Hello World.Goodbye!
The last char of s3: !
How can I modify my code to get s3 and last char of s3 correctly?
In many of your constructors, you do not set length which leaves it with an indeterminate value - and reading such values makes the program have undefined behavior. So, first fix that:
#include <algorithm> // std::copy_n
// Constructor with no arguments
String::String() : data{new char[1]{'\0'}}, length{0} {}
// Constructor with one argument
String::String(const char* s) { // note: const char*
if (s == nullptr) {
data = new char[1]{'\0'};
length = 0;
} else {
length = std::strlen(s);
data = new char[length + 1];
std::copy_n(s, length + 1, data);
}
}
// Copy Constructor
String::String(const String& source) : data{new char[source.length + 1]},
length{source.length}
{
std::copy_n(source.data, length + 1, data);
}
// Move Constructor
String::String(String&& source) : String() {
std::swap(data, source.data);
std::swap(length, source.length);
}
In operator+= you are trying to use the subscript operator, String::operator[], but you haven't added such an operator so instead of s[i], use s.data[i]:
String& String::operator+=(const String& s) {
unsigned len = length + s.length;
char* str = new char[len + 1];
for (unsigned j = 0; j < length; j++) str[j] = data[j];
for (unsigned i = 0; i < s.length; i++) str[length + i] = s.data[i];
str[len] = '\0';
delete[] data; // note: delete[] - not delete
length = len;
data = str;
return *this;
}
If you want to be able to use the subscript operator on String objects, you would need to add a pair of member functions:
class String {
public:
char& operator[](size_t idx);
char operator[](size_t idx) const;
};
char& String::operator[](size_t idx) { return data[idx]; }
char String::operator[](size_t idx) const { return data[idx]; }
And for String s3 = s1 + s2; to work, you need a free operator+ overload:
String operator+(const String& lhs, const String& rhs) {
String rv(lhs);
rv += rhs;
return rv;
}
Also, to support printing a String like you try in your alternative main function, you need an operator<< overload. Example:
class String {
friend std::ostream& operator<<(std::ostream& os, const String& s) {
os.write(s.data, s.length);
return os;
}
};
Full demo
For starters neither constructor sets the data member length.
So the operator
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
//...
has undefined behavior.
Also provided that the data member length was initialized you need to write
char* str = new char[len + 1];
instead of
char* str = new char[len];
to reserve memory for the terminating zero character '\0' because you are using the standard C string function strcpy in the copy constructor
strcpy(data, source.data);
And the class does not have the subscript operator used in this for loo[
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
And you forgot to append the terminating zero character '\0'.
Pay attention to that there is no member function size in the class used in this expression
s3[s3.size()-1]
And this construction
String s3 = String s1 + String s2;
is invalid. At least you should write
String s3 = s1 + s2;
and correspondingly define the operator +.

C++ Dynamic Allocation, Is my use of new correct?

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.

What's wrong with my String concatenation in C++?

I am learning C++ by creating a String class for an embedded project and I have a problem with my String class' concatenation.
Here is my main method and the output
#include <iostream>
#include "string.hpp"
using namespace std;
int main() {
String s1("hello "), s2("world");
String s3 = s1 + s2;
cout << "s1=" << s1 << endl;
cout << "s2=" << s2 << endl;
cout << "s3=" << s3 << endl;
return 0;
}
s1=hello
s2=world
s3=hello
Rather than print out "hello world", it prints just "hello "
Here is my string.hpp class:
#pragma once
#include <cstring>
#include <iostream>
class String {
public:
String() : c_str(NULL), len(0)
{
}
String(const String& str) : c_str(new char[str.len]), len(str.len)
{
strncpy(c_str, str.c_str, len);
c_str[len] = '\0';
}
String(const char* str) : String(str, strlen(str))
{
strncpy(c_str, str, len);
c_str[len] = '\0';
}
String(const char* str, const int n) : len(n), c_str(new char[n+1])
{
strncpy(c_str, str, len);
}
~String()
{
delete[] c_str;
}
const char* get_c_str()
{
return c_str;
}
bool contains(const String &cmd, const size_t pos)
{
return strncmp(c_str+pos, cmd.c_str, cmd.len) == 0;
}
size_t length()
{
return len;
}
friend std::ostream& operator<<(std::ostream& os, const String obj)
{
os << obj.c_str;
return os;
}
friend void swap(String& s1, String& s2)
{
using std::swap;
swap(s1.c_str, s2.c_str);
swap(s1.len, s2.len);
}
bool operator==(const String& str)
{
return strncmp(c_str, str.c_str, len) == 0;
}
char operator[](const size_t i)
{
return c_str[i];
}
String& operator=(const String& src)
{
String tmp(src);
swap(*this, tmp);
return *this;
}
String operator+(const String& rhs)
{
const size_t new_len = len + rhs.len;
char* new_c_arr = new char[new_len+1];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs.c_str);
printf("new_c_arr=%s\n", new_c_arr);
return String(new_c_arr, len);
}
String operator+(const char* rhs)
{
const size_t new_len = len + strlen(rhs) + 1;
char* new_c_arr = new char[new_len];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs);
return String(new_c_arr, new_len);
}
private:
char* c_str;
int len;
};
I read about "the big 3" on SO while looking for a similar question and not sure if it because of this.
This code is broken in numerous ways.
#pragma once
Actual include guards are more portable than #pragma once.
String() : c_str(NULL), len(0)
{
}
Your default constructor makes c_str null; your other functions never check for this case. Remember that even an empty C string has one character.
String(const String& str) : c_str(new char[str.len]), len(str.len)
{
strncpy(c_str, str.c_str, len);
c_str[len] = '\0';
}
You only allocated str.len characters for c_str, yet you are accessing c_str[len].
String(const char* str) : String(str, strlen(str))
{
strncpy(c_str, str, len);
c_str[len] = '\0';
}
The constructor to which you delegated already performed a copy. Why are you calling strncpy here again?
String(const char* str, const int n) : len(n), c_str(new char[n+1])
{
strncpy(c_str, str, len);
}
and here you didn't make sure that your string is null-terminated.
const char* get_c_str()
{
return c_str;
}
Should be marked const.
bool contains(const String &cmd, const size_t pos)
{
return strncmp(c_str+pos, cmd.c_str, cmd.len) == 0;
}
Ditto. And you didn't check that pos is in range.
size_t length()
{
return len;
}
const.
friend std::ostream& operator<<(std::ostream& os, const String obj)
{
os << obj.c_str;
return os;
}
obj should be passed by const reference, not by value.
bool operator==(const String& str)
{
return strncmp(c_str, str.c_str, len) == 0;
}
const again, and the logic isn't even correct. By this logic, "something" == "something else" since you are only comparing the first len characters.
char operator[](const size_t i)
{
return c_str[i];
}
If you are returning a copy, then it should be const. If you want to allow users to modify the characters stored in the string, then it should return a char &. (Even better, have two separate overloads, one const and one non-const.)
String operator+(const String& rhs)
{
const size_t new_len = len + rhs.len;
char* new_c_arr = new char[new_len+1];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs.c_str);
printf("new_c_arr=%s\n", new_c_arr);
return String(new_c_arr, len);
}
You are constructing the new string with the wrong length, and also leaking new_c_arr. And this function should be const (and really should be implemented in terms of operator+=, which you don't have).
String operator+(const char* rhs)
{
const size_t new_len = len + strlen(rhs) + 1;
char* new_c_arr = new char[new_len];
strcpy(new_c_arr, c_str);
strcat(new_c_arr, rhs);
return String(new_c_arr, new_len);
}
Leaking new_c_arr again; also, new_len here includes the null terminator, while the version in the other operator+ doesn't. Your constructor taking a length does not appear to include the null terminator as part of the length.
This is the line of problem in the method String operator+(const String& rhs):
return String(new_c_arr, len);
You need to changed to this:
return String(new_c_arr, new_len+1);
You are initializing the returning String with a length equal to the first part only. Not the whole concatenated string.
See that the other operator is OK.
By the way you're creating a lot of new char[] in the operator+ and the constructor.
In the case of the operator+ you are creating a new char[] in every call and passing to the constructor of string (not deleting after), and in the constructor are creating a new char[] to storage the string that is deleted in the destructor, the new char[] created in the operators are leaked.

C++ string implementation error

I am trying to implement a basic string class in C++. However, I am stuck on the append() function. It would like it to be Hello World, but it results as Hello ÍWorlýýýý««««««««þ:
#define __START namespace lib{
#define __END }
__START
class string
{
public:
string(const char* s)
{
_str = s;
}
const char operator[](int position)
{
return _str[position];
}
void operator=(string s)
{
_str = s.c_str();
}
void append(string s)
{
size_t nlength = (length() + s.length());
char* nstr = new char[nlength];
for (int i = 0; i < length(); ++i)
{
nstr[i] = _str[i];
}
for (int i = length() + 1; i < nlength; ++i)
{
nstr[i] = s[(i - length() - 1)];
}
_str = const_cast<const char*>(nstr);
}
void operator+=(string s)
{
append(s);
}
const char* c_str()
{
return _str;
}
size_t length()
{
return strlen(_str);
}
std::string str()
{
return _str;
}
private:
const char* _str;
};
__END
int main()
{
lib::string s = "Hello ";
s.append("World"); // s += "World";
std::cout << s.c_str();
getchar();
}
There are a lot of errors, not only with append
string(const char* s)
{
_str = s;
}
The constructor is wrong, you should make a copy of s in order to free it later, this way:
~string()
{
delete[] _str; // or free(_str) in case you use malloc/realloc, thanks Fred!
}
Private member variable:
private:
const char* _str;
The internal string should not be const, you should be able to resize it later
const char operator[](int position)
{
return _str[position];
}
You are missing a check: length() > position
void operator=(string s)
{
_str = s.c_str();
}
You are not modifying s, it should be const string& s
You are also not copying s.c_str() which means that now s and this are sharing the same internal buffer
void append(string s) // s should be a const reference too
{
size_t nlength = (length() + s.length());
char* nstr = new char[nlength];
for (int i = 0; i < length(); ++i)
{
nstr[i] = _str[i];
}
for (int i = length() + 1; i < nlength; ++i)
{
nstr[i] = s[(i - length() - 1)];
}
_str = const_cast<const char*>(nstr);
}
Should be easier to write in terms of realloc:
void append(string s)
{
int start = length();
_str = realloc(_str, length() + s.length());
for (int i = 0; i < s.length(); i++) {
_str[start+i] = s[i];
}
}
If you want to stick to new its OK, but you must free _str before assigning it to the new one.
The following operators should be const:
const char* c_str() const;
size_t length() const;
std::string str();
Update: Options for the constructor:
// option one (use delete[] to cleanup _str)
string(const char* s) {
int n = strlen(s);
_str = new char[n+1];
memcpy(_str, s, n+1); // s is NULL terminated
}
// option two (use free() to cleanup _str)
string(const char* s) {
int n = strlen(s);
_str = (char*)malloc(n+1);
memcpy(_str, s, n+1); // s is NULL terminated
}
// option 3: rely on append taking a char* argument
string(const char *s) : _str(NULL) {
append(s, strlen(s));
}
..
void append(const string& s) {
append(s.c_str(), s.length())
}
void append(const char *s, int len) {
int start = _str ? length() : 0;
_str = realloc(_str, start + len);
for (int i = 0; i < len; i++) {
_str[start+i] = s[i];
}
}
Update 2: It will be better to use size_t or unsigned int instead of plain int because the size is always greater than or equal to zero.
There are tons of problems with your code, so let's do this step by step.
First, you don't need your preprocessor magic for the namespace, but just a basic namespace mynamespace{}.
Secondly, it is ideal to create a basic_string class, so that it can be used with different character types. char / wchar_t, e.t.c.
Problems with your class:
1) private: const char *_str;. The pointer will be modified, so the const is useless.
2) There are no typedefs. You need them if you are attempting to re-implement a STL class. (Explained in example)
3) Use an allocator. This way you can construct, and destroy elements, allocate, and deallocate memory. You will be certain to keep your memory a lot safer this way.
4) Strings must be null terminated. That means an extra '\0' at the end, meaning you will have to allocate an extra byte for that. Strings are null terminated, because it is a way of telling the code to stop reading the string.
5) You are assigning to a string which has not been allocated. _str = s.c_str(); could easily crash, depending on your compiler, because you are writing to non-allocated memory.
6) Use const references, rather than normal types for your parameters (string = const string &). You also need this for your copy constructor basic_string(const _Myt &).
I still may not have highlighted all of the problems
Example basic_string class
template < typename _Elem, typename _Traits = std::char_traits<_Elem>, typename _Alloc = std::allocator<_Elem> > class basic_string
{
public:
typedef basic_string<_Elem, _Traits, _Alloc> _Myt;
typedef _Elem value_type;
typedef _Traits traits_type;
typedef _Alloc allocator_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef value_type *iterator;
typedef const value_type *const_iterator;
typedef value_type &reference;
typedef const value_type &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
basic_string()
{
__data = _Alloc().allocate(1);
_Alloc().construct(&__data[0], '\0');
}
basic_string(const_pointer _Init)
{
int count = 0;
__data = _Alloc().allocate(_Traits::length(_Init) + 1);
for (const_iterator i = &_Init[0]; i != &_Init[_Traits::length(_Init)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[_Traits::length(_Init)], '\0');
}
basic_string(const _Myt &_Init)
{
if (this != &_Init)
{
int count = 0;
__data = _Alloc().allocate(_Traits::length(_Init.__data) + 1);
for (const_iterator i = &_Init.__data[0]; i != &_Init.__data[_Traits::length(_Init.__data)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[_Traits::length(_Init.__data)], '\0');
}
else
{
__data = _Alloc().allocate(1);
_Alloc().construct(&__data[0], '\0');
}
}
~basic_string()
{
if (__data)
{
size_type tmp = size();
for (iterator i = begin(); i != end(); ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(__data, tmp);
}
}
_Myt &assign(const_pointer _Rhs)
{
int count = 0;
reserve(_Traits::length(_Rhs) + 1);
for (const_iterator i = &_Rhs[0]; i != &_Rhs[_Traits::length(_Rhs)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[_Traits::length(_Rhs)], '\0');
return *this;
}
_Myt &operator=(const_pointer _Rhs)
{
return assign(_Rhs);
}
_Myt &append(const_pointer _Rhs)
{
int count = size();
reserve(size() + _Traits::length(_Rhs) + 1);
for (const_iterator i = &_Rhs[0]; i != &_Rhs[_Traits::length(_Rhs)]; ++i, ++count)
{
_Alloc().construct(&__data[count], *i);
}
_Alloc().construct(&__data[count], '\0');
return *this;
}
_Myt &operator+=(const_pointer _Rhs)
{
return append(_Rhs);
}
iterator begin()
{
return &__data[0];
}
iterator end()
{
return &__data[size()];
}
size_type size()
{
return _Traits::length(__data);
}
_Myt &swap(basic_string<_Elem> &_Rhs)
{
std::swap(__data, _Rhs.__data);
return *this;
}
void reserve(size_type _Size)
{
int count = 0;
if (_Size < size())
{
return;
}
pointer buf = _Alloc().allocate(_Size);
for (iterator i = begin(); i != end(); ++i, ++count)
{
_Alloc().construct(&buf[count], *i);
}
std::swap(__data, buf);
for (iterator i = &buf[0]; i != &buf[_Traits::length(buf)]; ++i)
{
_Alloc().destroy(i);
}
_Alloc().deallocate(buf, _Traits::length(buf));
}
operator const_pointer()
{
return __data;
}
operator pointer()
{
return __data;
}
template < typename _Traits1, typename _Alloc1 > friend std::basic_ostream<_Elem> &operator<<(std::basic_ostream<_Elem> &_Stream, basic_string<_Elem, _Traits1, _Alloc1> &_Str)
{
return _Stream << _Str.c_str();
}
const_pointer data() const
{
return __data;
}
const_pointer c_str() const
{
return __data;
}
private:
pointer __data;
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
Of course, there is still a bit missing, but I'm sure you can implement that with a little online help.
First of all, your most critical problem is in char* nstr = new char[nlength];.
You must change it to char* nstr = new char[nlength+1];.
Then, in function append, after the two for loops, set nstr[nlength] = 0;
Second, for better performance (as well as correct coding), you probably need to change string s to const string& s in the following functions:
void operator=(string s)
void append(string s)
void operator+=(string s)
You have an off-by-one error in the second loop; the second string needs to be copied to length(), immediately after the end of the first:
for (int i = length(); i < nlength; ++i)
{
nstr[i] = s[i - length()];
}
You'll also need to allocate one more byte to put a null terminator on the end.
Note that you don't need that scary-looking cast to add const to the pointer, since that's a perfectly safe thing to do. const_cast is only needed to remove qualifiers. You might also want to fix the memory leak and, for bonus points, cache the length so you don't have to read the entire string every time you want it.
for (int i = length() + 1; i < nlength; ++i)
{
nstr[i] = s[(i - length() - 1)];
}
You first copy chars up to length()-1 and then start back at length() + 1, so you skip a char and there could be anything in there after allocation(unless you do a memset beforehand).
Then you need to terminate your string with a null character(\0) so that the std::string that gets constructed on str() method return knows when to stop reading chars.

user defined string::reserve()

So we are writing a bunch of user defined functions that mimic the string class member functions. I am stuck on reserve. It looks like it should work but I am missing something. I have tried a couple of diffrent variations such as putting this-> in front of the member varibles or my string:: in front of them... my out put does not change but throught cout statment placed within the function I know it access the function.
here is the code
#include <cstdlib>
#include <iostream>
#include <string>
#include "mystring.h"
#define string mystring
using namespace std;
void check (string s, string name){
cout << "checking " << name << endl;
cout << name << " contains " << s << endl;
cout << name << " capacity() is " << s.capacity() << endl;
cout << name << " length() is " << s.length() << endl;
cout << name << " size() is " << s.size() << endl;
cout << name << " max_size() is " << s.max_size() << endl << endl;
}
int main(int argc, char** argv) {
cout<<"This is Lab 5"<<endl;
string s1("Hello, World!");//step 2
string s1name("s1");//step 3
check(s1,s1name);//step 4
check(s1,s1name);//step 5
cout << "---Testing assignment operator---\n\n";
{
string s2;
s2=s1;
string s2name("s2");
check(s2,s2name);
if(s1==s2)
cout<<"comparison true\n";
else
cout<<"comparison false\n";
}
check(s1,s1name);
string s3("check assignment");
s3=s3;//checking to see if operator= is used when they are the same object.
check(s3,"s3");
cout<<"Lab 5 ends"<<endl;//step6
// //clear check
// s3.clear();
// check(s3,"s3");
// if(s1==s3)
// cout<<"comparison true\n";
// else
// cout<<"comparison false\n";
// reserve check
// mystring::size_type res;
// res=40;
s3.reserve(40);//still working on reserve
check(s3,"s3");
cout<<"in main buf size"<<s3.capacity()<<endl;
s3.reserve(5);
check(s3,"s3");
// char* test=s3.begin();
// cout<<&test<<endl;
// cout<<&s3<<endl;
//empty check
// string s4;
//
// if (s4.empty())
// cout<<"Empty is true\n";
// else
// cout<<"Empty is false\n";
return 0;
}
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <math.h>
#include <cstring>
using namespace std;
class mystring {
public:
// types with scope in this class
typedef unsigned int size_type;
typedef char * iterator;
typedef const char * const_iterator;
static const long int npos = 1073741824;
// default constructor
mystring();//good
// other constructors
mystring(const char *);//good
// copy constructor
mystring(const mystring& orig);//
// destructor
~mystring();////
// iterators
iterator begin();//good
iterator end();//good
//=== memory related ===
// change buffer size to n
void reserve(size_type n);
size_type size() const;////good returns len
size_type length() const;////good returns len
size_type capacity() const;////good returns buf_size
size_type max_size() const;////good
bool empty() const;////good
//=== overloading operators ===
// assignment operator
mystring& operator=(const mystring&);////
// mystring& operator=(const char *);
// array notation
char operator[](size_type pos) const;
char& operator[](size_type pos);
// append
mystring& operator+=(const mystring& str);
mystring& operator+=(const char * str);
//=== methods that modifiy the string
void clear();////good
void push_back(char c);
mystring& append(const mystring& str);
mystring& append(const char * str);
mystring& insert(size_type pos, const mystring& str);
mystring& insert(size_type pos, const char * str);
mystring& replace(size_type start, size_type span, const mystring& str);
mystring& replace(size_type start, size_type span, const char * str);
//=== conversion to c string
const char * c_str() const;//
private:
// pointer to the memory location where string is stored as a c-style
// string
char * ptr_buffer;
// the size of the memory in terms of bytes or characters capable of going into it currently
size_type buf_size;
// number of characters currently in the memory not including the
// terminating null character
size_type len;
};
#include "mystring.h"
// default constructor provided for lab 5
mystring::mystring() {
ptr_buffer = new char[1];
*ptr_buffer = '\0';
buf_size = 1;
len = 0;
}
// constructor from c-style string or "abc" provided for lab 5
mystring::mystring(const char * s) {
len = strlen(s);
buf_size = len + 1;
ptr_buffer = new char[buf_size];
strcpy(ptr_buffer, s);
}
// copy constructor to be implemented in lab 5
mystring::mystring(const mystring& orig) {
len=orig.length();
ptr_buffer=new char[len+1];
buf_size=len+1;
for(int n=0 ;n<buf_size; n++ )
{
ptr_buffer[n]=orig.ptr_buffer[n];
}
ptr_buffer[buf_size]='\0';
}
void mystring::reserve(size_type n)
{
cout<<"cccccc:"<<capacity()<<endl;
if( n > capacity() )
{
const char* temp = ptr_buffer;
ptr_buffer = new char[n];
memcpy(ptr_buffer, temp, len+1);
delete [] temp;
buf_size=n;
cout<<"bbbbbuf size"<<buf_size<<endl;
}
// char *temp;
//
// temp=new char[n];
//
// int i=0;
//
// for(;i<=len;i++)
// {
// temp[i]=ptr_buffer[i];
//
// }
// buf_size=n;
//
// delete [] ptr_buffer;
//
// ptr_buffer=temp;
//
}
mystring::iterator mystring::begin()//think is working correctly
{
iterator it=ptr_buffer;
return it;
}
mystring::iterator mystring::end()//think is working correctly
{
iterator it=ptr_buffer+len;
return it;
}
// one of the over loaded assignment operator to be implemented // assignment 3 (or for lab 5 if you have more time)
mystring& mystring::operator=(const mystring& orig){
if(this!=&orig)
// {
// cout<<"this==&mystring if statment activated\n";//comment out after testing
// break;
// }
{
delete this->ptr_buffer;
this->len=orig.len;//length();
this->ptr_buffer=new char((this->len)+1);
this->buf_size=(this->buf_size)+1;
cout<<"Using assignment operator="<<endl;
for(int n=0;n<this->buf_size;n++)
{
this->ptr_buffer[n]=orig.ptr_buffer[n];
}
this->ptr_buffer[buf_size]='\0';
return *this;
}
}
// some simple methods provided for lab 5
mystring::size_type mystring::size() const {
return len;
}
mystring::size_type mystring::length() const{
return len;
}
mystring::size_type mystring::capacity() const{
return buf_size;
}
mystring::size_type mystring::max_size() const{
return (int)pow(2,30) -4 ;
}
bool mystring::empty() const
{
if(len==0)
return true;
else
return false;
}
// destructor to free space implemented for lab 5
mystring::~mystring() {
delete [] ptr_buffer;
}
// provided for lab 5 so we may cout mystring
ostream& operator<<(ostream& out, const mystring& str) {
out << str.c_str();
return out;
}
// provided for lab 5 to support the implementation of <<
const char * mystring::c_str() const {
return ptr_buffer;
}
char mystring::operator[](size_type pos) const
{
return *(ptr_buffer+pos);
}
void mystring::clear()
{
char *temp;
temp=new char[1];
temp[0]='\0';
ptr_buffer=temp;
buf_size=0;
len=0;
}
void mystring::push_back(char c)
{
}
bool operator==(const mystring& lhs, const mystring& rhs)
{
if(lhs.length()==rhs.length())
{
for(int i=0; i<lhs.length();i++)
{
if(lhs[i]!=rhs[i])
return false;
}
return true;
}
return false;
}
So I put some cout statements in the reserve() function and in main. The reserve functions is changing the buffer size and capacity call is returning correctly but when we use the check function it is not showing that any change has occurred. I have put in revised code.
Ok, everyone. Turns out on the check function declared before main that it makes a copy of the mystring object. The copy constructor was defined to make buf_size= len + 1. So I turned it into buf_size=orig.buf_size. The reserve function probably worked as stated and yes it needed an if statment to make sure we were not shrinking it, but i was not worried about that yet. I used daves example with a little added code. thanks all for your help.
void mystring::reserve(size_type n)
{
if (n <= buf_size)
{
return;
}
from the MSDN basic_string::reserve Sets the capacity of the string to a number at least as great as a specified number.
So reserve can't be used to truncate a string
Here we are doing something stronger (we don't let the "user" shorten the buffer). Cleary you could
if (n <= len + 1) // remember the \0 terminator is not included in len
{
return;
}
and it would be correct.
But even the first form would be correct. There isn't any guarantee that reserve will "reduce" the size of a buffer.
In general, to copy between buffers, you shoud use memcpy with a length of len + 1. It's (normally) faster (no one requires it to be faster, but it is faster :-) )