Reciever80
Reciever80

Reputation: 105

Issues with Overloading '<<' operator

Creating a custom String class, and I'm having trouble overloading the '<<' operator for output. I've tried a few different ways of writing it (commented out in the code below, for reference and since they didn't work), and get various errors. For starters, the error I get from the code below is:

Error 1: error C2804: binary 'operator <<' has too many parameters line 53

Error 2: error C2333: 'String::operator <<' : error in function declaration; skipping function body line 53

Error 3: error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'String' (or there is no acceptable conversion) line 180 (There's a bunch of these)

Since the file is over 300 lines, and I doubt you guys need to read through it all, I'll --snip-- some of it, but if you want the 300 lines I'll be sure to edit this.

Also, I know that the 'using namespace std' in a header is bad practice. I'm sorry, this is an old file I'm working with, and I promise I won't include it in future header files.

That said, here's the code (and thanks a lot for the help):

As I said before, thanks a ton to those reading and responding below. I've spent close to two hours trying to find a viable solution to this, and the posts I've searched for have either been too complex for me to understand, or didn't apply in my case. From looking at the cplusplus website, I saw that the << operator function only takes one parameter, however was having issues making that work. If you guys post what you think would work, I'll try and it and reply back.

Thanks all! Let me know if I'm missing any critical information.

#pragma once

#include <iostream>
#include <sstream>
using namespace std;

#define NOT_FOUND -1

//ostream& operator << (ostream& out, const String& myString);

// C++ String class that encapsulates an ASCII C-string
class String
{
public:
    // Default constructor
    String()
    {
        Text = NULL;
    }
    
    // MUST HAVE: Copy-constructor that performs deep copy
    String(const String& source)
    {
        Text = NULL;
        // Call the assignment operator to perform deep copy
        *this = source;
    }
    
    // Init-constructor to initialize this String with a C-string
    String(const char* text)
    {
        Text = NULL;
        // Call the assignment operator to perform deep copy
        *this = text;
    }
    
    // Init constructor, allocates this String to hold the size characters
    String(int size)
    {
        Text = new char[size];
    }
    
    // Destructor
    ~String()
    {
        delete[] Text;
    }
    
    // This is what I would love to have work.
    ostream& operator << (ostream& out, const String& myString)
    {
        return out << myString.GetText();
    }
    // Returns a new string that corresponds to a substring of this String
// beginning at startPosition and length chars long;
// if length = 0 (not specified) then the substring spans from
// startPosition until the end of this String
// throws an exception when startPosition is out of bounds
String Substring(int startPosition, int length) const
{
    char * str = this->GetText();
    String returnString;
    int strLength = length;
    int x = 0;

    if(length == 0)
        strLength = GetLength(str)-startPosition;

    char* substring = new char[strLength];

    // Makes sure the starting position is within the bounds of the String 'this'
    try
    {
        CheckBounds(startPosition, str);
    }
    catch(char * error)
    {
        cout << error << endl << endl;
    }

    // In case the substring length is too long, it cuts short once it reaches the end of the original string. Yu-San, this is #2 on the three options you said I could implement, FYI.
    while(x < strLength && str[x+startPosition]!='\0')
    {
        substring[x] = str[x + startPosition];
        x++;
    }
    substring[x]='\0';
    //for(int x = 0; x<strLength; x++)
    //{
    //returnString = str + startPosition;
    returnString = substring;
    //}

    return returnString;    
}
    // Assignment operator to perform deep copy
    String& operator = (const char* text)
    {
        // Ddispose of old Text
        delete[] Text;
        
        // +1 accounts for NULL-terminator
        int trueLength = GetLength(text) + 1;
        
        // Dynamically allocate characters on heap
        Text = new char[trueLength];
        
        // Copy all characters from source to Text; +1 accounts for NULL-terminator
        for ( int i = 0; i < trueLength; i++ )
            Text[i] = text[i];
        
        return *this;
    }
    
    // Returns the count of characters in a C-string text; NULL-terminator is not counted
    // static means that the member function neither reads nor
    // writes any of the class' data members
    // String::GetLength("blah");
    static int GetLength(const char* text)
    {
        int x = 0;
        while(text[x] != '\0')
            x++;
        
        return x;
    }
    
    // Returns a reference to a single character from this String
    char& operator [] (int index) const
    {
        int length = GetLength();
        
        // Check for valid index
        if ( (index < 0) || (index > length) )
        {
            stringstream error;
            error << "operator[] - index " << index << " is out of bounds (0.." << (length - 1) << ")";
            throw String(error.str().c_str());
        }
        
        return Text[index];
    }
// Returns the count of characters in the String; NULL-terminator is not counted
int GetLength() const
{
    return GetLength(Text);
}



char* GetText() const
{
    return Text;
}

// Finds first index of a symbol not found in "text" in string *this
int FindFirstNotOf(char * text)
{
    String objectString(*this);
    int x = 0; // loop counter
    int firstIndex = 0; // index to return

    while(text[x]!='\0')
    {
        // runs each character in 'text' against each character in the object.
        for(int y = 0; y<objectString.GetLength(); y++)
        {
            if(objectString[x] == text[x])
                objectString[x] = '\0';
        }

        x++;
    }

    while(objectString[firstIndex]=='\0' && (firstIndex<objectString.GetLength()))
    {
        firstIndex++; //keeps running until it Finds a character that wasn't changed to \0
    }

    if(firstIndex == objectString.GetLength())
        firstIndex = -1; // means it wasn't found.

    return firstIndex;
}

int FindFirstOf(char iWantThis)
{
    String objectString(*this);
    int firstIndex = 0; // index to return

    while(objectString[firstIndex]!=iWantThis && (firstIndex<objectString.GetLength()))
    {
        firstIndex++; //keeps running until it Finds a character that wasn't changed to \0
    }

    if(firstIndex == objectString.GetLength())
        firstIndex = -1; // means it wasn't found.

    return firstIndex;
}

int FindLastOf(char iWantThis)
{
    String objectString(*this);
    int index = 0;
    int lastIndex = -1;

    while(objectString[index]!='\0')
    {
        if(objectString[index] == iWantThis)
            lastIndex = index;
    }

    return lastIndex;
}

// finds a desired char in String object, with default start index of 0
int Find (char iWantThis, int startIndex = 0)
{
    int index = -1;
    int ctr = startIndex;

    String objectString(*this);

    while(objectString[ctr]!='\0' && index == -1) // runs until it reaches end, or the return index is changed
    {
        if(objectString[ctr] == iWantThis)
            index = ctr;
        else
            ctr++;
    }

    return index;
}

private:

// Throws an String-type exception when index is out of bounds
void CheckBounds(int index, const char* source) const
{
    int size = GetLength(source);

    if(index < 0 && index >=size)
        throw "Selected Starting Index is Out of Bounds.";  
}

// The encapsulated C-string
char* Text;
};


// Stream output operator to print String to output stream
/* ostream& operator << (ostream& out, const String& myString)
 * {
 *  return out << myString.GetText();
 * };
 */

Upvotes: 2

Views: 1245

Answers (1)

WhozCraig
WhozCraig

Reputation: 66194

You need an insertion operator to be a free-function:

Inside your class body:

class String
{
   friend std::stream& operator <<(std::ostream&, const String&);
   ...
};

Outside of your class body:

inline std::ostream& operator << (std::ostream& os, const String& myString)
{
    os << myString.GetText();
    return os;
}

This assumes GetText() is a viable member function, which in your current code snippet is nowhere to be found. I assume you have one somewhere. If it is public then you need not declare the friend-relationship in the class body, and can just use the free-function insertion operator.

Finally, this assumes the operator is implemented in a header file (thus the inline). If you move the implementation to a .cpp file be sure to declare it as a global prototype in your header and remove the inline preamble in the .cpp implementation.

Side Note: Break the habit of putting using namespace std; in your header files right now. it is a bad practice and can have unwanted side effects for source files including your header that aren't expected that namespace exposure.

Upvotes: 2

Related Questions