Desktop Programming > C/C++ & Visual C++
An example of coding a C++ class MyString ...
(1/1)
David:
Update: FREE homework help NOW available ...
You can contact me via:
http://sites.google.com/site/andeveryeyeshallseehim/home/he-comes
http://developers-heaven.net/forum/index.php/topic,2587.0.html
An other fairly often student problem ...
is to code a class to take in and handle dynamic strings in C++,
perhaps, (under the hood), using arrays of char that are terminated by '\0' ( i.e. C strings ) ...
The following example may give you some ideas how to start ...
Firstly the .h file:
--- Code: ---// MyString.h // // 2016-02-05 //
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <cstring> // re. strlen, etc...
#include <cctype> // re. tolower
#include <cmath> // re. log10
#include <vector>
// a start to a NULL terminated C++ string class //
struct MyString // You can, the way coded here, just replace struct with class ... MAKES NO difference HERE !!! //
{
public:
// if neg num's stored as 'twos complement' ...
// this next value is the largest size_t possible //
static const size_t npos = -1;
static const size_t chunk_size = 255;
// 4 ctor's follow ...
// default ...
MyString() : len(0), str( new char[1] )
{
str[0] = 0;
}
// from C string ...
MyString( const char* s )
{
len = strlen(s);
str = new char[len+1];
strncpy( str, s, len );
str[len] = 0; // terminate with the (NULL C) '\0' char //
}
//
MyString( const size_t num, const char ch )
{
len = num;
str = new char[len+1];
for( size_t i = 0; i < len; ++ i ) str[i] = ch;
str[len] = 0; // terminate with the (NULL C) '\0' char //
}
// Copy ctor
MyString( const MyString& s )
{
len = s.len;
str = new char[len+1];
strncpy( str, s.str, len );
str[len] = 0; // terminate with the (NULL C) '\0' char //
}
// overloaded assignment i.e. overloaded operator = ...
MyString& operator = ( const MyString& s )
{
if( this != &s )
{
delete [] str; // firstly delete (free up) OLD memopry
len = s.len;
str = new char[len+1];
strncpy( str, s.str, len );
str[len] = 0; // terminate with the (NULL C) '\0' char //
}
return *this;
}
// overloaded operator += ...
MyString& operator += ( const MyString& s )
{
size_t n_len = len + s.len;
char* tmp = new char[n_len+1];
strncpy( tmp, str, len );
strncpy( tmp+len, s.str, s.len );
tmp[n_len] = 0; // terminate with '\0' char //
delete str; // delete old memory //
str = tmp; // update
len = n_len;
return *this;
}
// overloaded operator += ...
MyString& operator += ( const char ch )
{
char* tmp = new char[len+2];
strncpy( tmp, str, len );
tmp[len] = ch;
++len;
tmp[len] = 0; // terminate with '\0' char //
delete str; // delete old memory //
str = tmp; // update
return *this;
}
// overloaded operator +...
friend MyString operator + ( const MyString& a, const MyString& b )
{
MyString tmp(a);
return tmp += b;
}
friend MyString operator + ( const char ch, const MyString& b )
{
MyString tmp( 1, ch );
return tmp + b;
}
friend MyString operator + ( const MyString& a, const char ch )
{
MyString tmp( 1, ch );
return a + tmp;
}
// overloaded operator []
char operator [] ( const size_t i ) const
{
return str[i];
}
// overloaded operator []
char& operator [] ( const size_t i )
{
return str[i];
}
char* c_str() const
{
return str;
}
char*& c_str()
{
return str;
}
size_t find( const char c, const size_t start = 0 )
{
for( size_t i = start; i < len; ++ i )
{
if( str[i] == c ) return i;
}
return npos;
}
MyString& erase( const size_t start, const size_t num = npos )
{
if( start > len )
return *this;
size_t numErase = num;
if( numErase > len - start )
numErase = len - start;
size_t n_len = len - numErase;
char* tmp = new char[n_len+1];
if( start ) // if ( start > 0 ) //
{
strncpy( tmp, str, start );
tmp[start] = 0; // 'NULL terminate' //
if( numErase < len - start )
strcat( tmp, str+start+numErase );
}
else // here ... start is 0 //
{
strncpy( tmp, str + numErase, len - numErase );
tmp[n_len] = 0;
}
delete [] str;
str = tmp;
len = n_len;
return *this;
}
MyString substr( const size_t start, const size_t num = npos )
{
MyString tmp;
if( start > len )
return tmp;
tmp.len = num;
if( tmp.len > len - start )
tmp.len = len - start;
delete [] tmp.str;
tmp.str = new char[tmp.len+1];
strncpy( tmp.str, str+start, tmp.len );
tmp.str[tmp.len] = 0; // 'NULL terminate' //
return tmp;
}
~MyString() // destructor
{
delete [] str;
str = 0;
len = 0;
}
void clear()
{
delete [] str;
str = new char[1];
str[0] = 0;
len = 0;
}
size_t size() const
{
return len;
}
private:
size_t len;
char* str;
friend std::ostream& operator << ( std::ostream& os, const MyString& ms )
{
return os << ms.str;
}
friend std::istream& operator >> ( std::istream& is, MyString& ms )
{
size_t cap = MyString::chunk_size, size = 0; // c to hold each char
char c;
char* strData = new char[cap+1];
while( is.get(c) && strchr(" \t\n", c) )
; // advance over all leading (ws) delimit char's //
if( !is.eof() ) // ok ... get this first char ... //
strData[size++] = c;
else
{
delete [] ms.str;
delete [] strData;
ms.str = new char[1];
ms.str[0] = 0;
ms.len = 0;
return is;
}
while( is.get(c) && !strchr(" \t\n", c) )
{
if( size == cap )
{
cap += cap; // double memory capacity
char* tmp = new char[cap+1];
for( size_t i = 0; i < size; ++ i )
tmp[i] = strData[i];
tmp[size] = 0;
delete [] strData;
strData = tmp;
}
// now ... since reached here ...
strData[size++] = c; // append this char //
}
strData[size] = 0; // ensure terminated with 0 //
if( cap > size ) // 'right' size string //
{
char* tmp = new char[size+1];
for( size_t i = 0; i < size; ++ i )
tmp[i] = strData[i];
tmp[size] = 0;
delete [] strData;
strData = tmp;
}
delete [] ms.str;
ms.str = strData;
ms.len = size;
return is;
}
friend std::istream& getline( std::istream& is, MyString& ms, const char* delimits = "\n" )
{
size_t cap = MyString::chunk_size, size = 0; // c to hold each char
char c;
char* strData = new char[cap+1];
while( is.get(c) && !strchr( delimits, c ) )
{
if( size == cap )
{
cap += cap; // double memory capacity
char* tmp = new char[cap+1];
for( size_t i = 0; i < size; ++ i )
tmp[i] = strData[i];
tmp[size] = 0;
delete [] strData;
strData = tmp;
}
// now ... since reached here ...
strData[size++] = c; // append this char //
}
strData[size] = 0; // ensure terminated with 0 //
if( cap > size ) // 'right' size string //
{
char* tmp = new char[size+1];
for( size_t i = 0; i < size; ++ i )
tmp[i] = strData[i];
tmp[size] = 0;
delete [] strData;
strData = tmp;
}
delete [] ms.str;
ms.str = strData;
ms.len = size;
return is;
}
void friend split( std::vector< MyString >& vms, const MyString& ms, const char* delimits = " \t" )
{
size_t len = ms.len, i = 0, j = 0;
while( i < len )
{
// skip over any leading delimits char's ...
while( strchr( delimits, ms.str[i] ) )
++ i;
if( i == len ) return; // ALL delimits //
j = i;
// find j 'one past' endof 'word' ...
while( j < len && !strchr( delimits, ms.str[j] ) )
++ j;
MyString tmp( j-i, ' ' ); // get right sixed empty MyString
strncpy( tmp.str, ms.str+i, j-i );
vms.push_back( tmp ); // add this word to end of vector //
i = j+1;
}
}
} ;
// EIGHT utilities for the above MyString type ... //
MyString takeInMyString( const MyString& prompt = "" )
{
std::cout << prompt << std::flush;
MyString s;
getline( std::cin, s );
return s;
}
int takeInChr( const MyString& prompt = "" )
{
return takeInMyString( prompt )[0];
}
// defaukts to 'y' i.e. YES //
bool more( const MyString& msg_part = "" )
{
std::cout << "More " + msg_part;
return tolower( takeInChr( " (y/n) ? " )) != 'n';
}
MyString& toCapsOnAllFirstLetters( MyString& str )
{
// to get CAPS on first char ...
int prev_was_ws = 1; // start with previous was white space
size_t len = str.size();
for( size_t i = 0; i < len; ++ i )
{
if( prev_was_ws )
str[i] = toupper( str[i] );
prev_was_ws = isspace( str[i] );
}
return str;
}
MyString& toUpper( MyString& str )
{
size_t len = str.size();
for( size_t i = 0; i < len; ++ i )
str[i] = toupper( str[i] );
return str;
}
MyString& toLower( MyString& str )
{
size_t len = str.size();
for( size_t i = 0; i < len; ++ i )
str[i] = tolower( str[i] );
return str;
}
// NO ERROR checking done here !!!
// The assumption here IS that the string passed in ...
// IS VALID ... for the type requested !!! //
// Note!!! Does NOT handle scientific notation !!! //
template< typename T >
T toNumber( const MyString& val )
{
MyString str( val );
char sign = '+';
if( str[0] == '-' )
{
sign = '-';
str.erase(0, 1);
}
else if(str[0] == '+')
str.erase(0, 1);
MyString dec_part;
double dec = 0.0;
size_t pos = str.find('.');
if( pos != MyString::npos )
{
dec_part = str.substr(pos+1);
str = str.erase(pos);
//std::cin.get();
double divisor = 1;
size_t len = dec_part.size();
for( size_t i = 0; i < len; ++ i )
{
divisor *= 10;
dec += (dec_part[i] - '0')/divisor;
}
}
size_t len = str.size();
T num = 0;
for( size_t i = 0; i < len; ++ i )
num = 10*num + str[i] - '0';
if( dec_part.size() )
num += dec;
if( sign == '-' )
num *= -1;
// else ...
return num;
}
// this handles conversion from NON-NEGATIVE INTEGER NUMBERS
// to a comma formatted string //
template< typename T >
MyString commasAdded( T val ) // val is a local copy //
{
int num_digits = (int) log10((double)val) +1;
MyString tmp( num_digits, '0' ); // get string correct size //
//std::cout << "Num digits is " << num_digits << '\n';
int i = num_digits-1; // start from end and work to front //
do
{
int digit = val % 10;
tmp[i] = '0' + digit;
val /= 10;
--i;
}
while( val );
int comma = 0,
len = tmp.size();
// note integer division here: 2/3 = 0 & 3/3 = 1
int len2 = len + (len-1)/3;
// construct right size and filled with commas
MyString tmp2( len2, ',' );
while( len2 ) // start at end of string of commas ... then ...
{
--len, --len2;
++comma; // this tracks where we are //
if( comma != 4 ) // keep copying until reach the next comma to skip over //
tmp2[len2] = tmp[len];
else // ha ... we want to LEAVE this comma here //
{
comma = 1; // reset counter
//tmp2[len2] = ','; // this comma STAYs NOT over-ridden //
--len2; // so skip over //
tmp2[len2] = tmp[len]; // ok ... copy in/(and over commas) this digit
}
}
return tmp2; // return it //
}
#endif
--- End code ---
David:
Now here is a little test program to start to test out the class MyString ...
--- Code: ---// testing_MyString.cpp // // 2016-02-05 //
// a demo of loops, arrays of char, new and delete (for arrays of char)
// recall that C strings ARE arrays of char terminated by a 'NULL char'
// i.e. arrays of char ... terminated by '\0'
#include "MyString.h"
#include <fstream>
#include <iomanip> // re. precision ...
////////////////////////////////////////////////////////////
// only used here to see AND to compare
// sizeof C++ string object
// with ...
// sizeof MyString object
#include <string>
using std::string;
////////////////////////////////////////////////////////////
const char* FNAME = "words.txt";
// just a few words as per below to test reading from file //
/*
sam joe bill harry ann jill bonnie
*/
int main()
{
using std::cout;
using std::endl;
using std::vector;
const size_t num_bytes = sizeof(size_t);
cout << "Number of bytes in type size_t is " << num_bytes << '\n';
// form the largest number that fits into a memory block with size of type size_t ...
size_t t = 1;
for( size_t i = 1; i < num_bytes*8; ++ i )
t = (t << 1) + 1; // shift left 1 byte and then add in 1 //
cout << "Largest size_t = " << commasAdded( t ) << '\n';
cout << "MyString::npos = " << commasAdded( MyString::npos ) << "\n\n";
cout << "sizeof(MyString) = " << sizeof(MyString) << '\n'
<< "sizeof(MyString*) = " << sizeof(MyString*) << '\n'
<< "sizeof(string) = " << sizeof(string) << '\n'
<< "sizeof(string*) = " << sizeof(string*)
<< "\n\n";
cout << "\nTesting toNumber(12345.67890) ...\n";
cout << std::setprecision(16);
double nVal = toNumber< double >( "12345.67890" );
cout << nVal<< "\n\n";
MyString s1, s2(" is working very diligently at U!" );
cout << "'" << s1 << "'" << " has len " << s1.size() << '\n';
cout << "'" << s2 << "'" << " has len " << s2.size() << '\n';
s1 = "Sam";
cout << "'" << s1 << "'" << " has len " << s1.size() << '\n';
s1 += s2;
cout << "'" << s1 << "'" << " has len " << s1.size() << '\n';
MyString s3( s1 + " True!!!" );
cout << "'" << s3 << "'" << " has len " << s3.size() << '\n';
for( size_t i = 0; i < s3.size(); ++ i ) cout << s3[i];
cout << endl;
size_t i = s3.find( 'w' );
if( i != MyString::npos )
{
s3[i] = 'l';
s3[i+1] = 'u';
s3.erase( s3.size() - 7 );
s3 += "Ha! Haaahhh!!!";
for( size_t i = 0; i < s3.size(); ++ i ) cout << s3[i];
}
cout << "\nTesting using getline with split (splitting up line) ...\n";
MyString test_line = takeInMyString( "Enter a line of text: " );
vector< MyString > vms;
split( vms, test_line );
for( size_t i = 0; i < vms.size(); ++ i )
cout << "'" << vms[i] << "' ";
cout << endl;
cout << "\n\nTesting reading file with 7 word using fin >> MyStringVar in a loop ... \n";
std::ifstream fin( FNAME );
int count = 0;
if( fin )
{
MyString ms;
while( fin >> ms )
{
++count;
cout << " (" << count << ") " << ms;
}
fin.close();
cout << endl;
}
else cout << "Ther was a problem opening file " << FNAME << '\n';
cout << "\n\n";
do
{
MyString name = takeInMyString( "Enter ALL your names (include middle name) : " );
cout << "You entered: \"" << name << "\"\n";
toCapsOnAllFirstLetters( name );
MyString mid;
// erase 2nd word of more than 2 words ... IFF exist //
size_t i = name.find( ' ' );
if( i != MyString::npos )
{
size_t j = name.find( ' ', i+1 ); // start at i+1 //
if( j != MyString::npos )
{
mid = name.substr( i+1, j-i );
name.erase( i, j-i );
}
}
name = "Hi there, " + name + '!';
cout << name << '\n';
if( mid.size() )
cout << "Your middle name is " << mid << '\n';
// print after erasing 1st 3 char's ...
cout << name.erase(0, 3) << '\n';
// erase next middle word ... IFF exists //
i = name.find( ',' );
if( i != MyString::npos )
{
size_t j = name.find( ' ', i+2 );
if( j != MyString::npos )
name.erase( i, j-i);
}
cout << name << '\n';
}
while( more( "names" ) );
}
--- End code ---
Navigation
[0] Message Index
Go to full version