1

I have created class and trying to overload ostream operator using a friend function but my friend is not able access private members of functions. Please help me figure out the problem. I have created another friend function named doSomething() from here I can call private member but not from overloaded function. Please help me updating overloaded stream insertion function.

Class Header File:

#pragma once
#ifndef MYSTRING_H
#define MYSTRING_H


class MyString
{
    char* str; 
    int length;
public:
    MyString();
    MyString(const char*);
    ~MyString();
    MyString (const MyString &Obj);
    void Display() const;
    int GetLength() const;
    const MyString& operator = (const MyString&);
    friend ostream& operator << (ostream &output,MyString&s);
    friend void doSomething();
};
#endif

Class CPP File:

#define _CRT_SECURE_NO_WARNINGS
#include "MyString.h"
#include <iostream>

using namespace std;

MyString::MyString() {
    length = 0;
    str = new char[1];
    str[length] = '\0';
}

MyString::MyString(const char* cString) {
    length=strlen(cString);
    str = new char[length+1];
    strcpy(str,cString);
    str[length] = '\0';
}

MyString::~MyString() {
    delete[] str;
    str = nullptr;
    length = 0;
}

MyString::MyString(const MyString& Obj) {
    
    length = Obj.length;
    str = new char[length+1];
    strcpy(str, Obj.str);
    str[length] = '\0';
}

void MyString::Display() const {
    cout << str << endl;
}

int MyString::GetLength() const  {
    return length;
}

const MyString& MyString::operator = (const MyString& Obj) {
    this->~MyString();
    length = Obj.length;
    str = new char[length + 1];
    strcpy(str, Obj.str);
    return *this;
}

Main CPP:

#define _CRT_SECURE_NO_WARNINGS
#include "MyString.h"
#include <iostream>
using namespace std;



ostream &operator << (ostream& output, MyString s) {
        
        output << s.length;
        return output;
}
void doSomething() {
    MyString s;
    s.length;
}

int main() {

    return 0;
}
  • 1
    Can you paste your exact *main.cpp* file? The program seems to be working when i compile it with gcc on my machine. –  Dec 26 '21 at 15:34
  • Other than a missing `#include ` this works fine when all in one file: https://ideone.com/xKrEi1 You should include the proper headers in your header file though and avoid `using namespace std;`. Consider a [mcve] and tell us which compiler you're using. The example in the link works in gcc and Visual Studio 2022. – Retired Ninja Dec 26 '21 at 15:35
  • the issue here is that you are not using std::ostream in your friend function definition, and just ostream is not defined, so it doesn't refet to the friend function and rather overloads it – laenNoCode Dec 26 '21 at 15:36
  • `this->~MyString();` -- Explain why you are calling the destructor. What C++ book shows calling the destructor for this purpose? Instead of this, this is much simpler: `{ MyString temp(Obj); std::swap(temp.str, str); std::swap(temp.length, length); return *this;}` Just 3 simple lines of code. – PaulMcKenzie Dec 26 '21 at 15:49
  • @paulMckenzie there not problem with that – Zain Ul Abiden Iftikhar Dec 26 '21 at 15:52
  • @ZainUlAbidenIftikhar There are problems with it. You are explicitly calling the destructor, meaning the object is in an invalid state right after that line of code. The destructor is a special function that you should never call explicitly, unless you are using something called `placement-new`, which you are not using. Just because you don't see the effects of that line of code doesn't mean it isn't wrong. – PaulMcKenzie Dec 26 '21 at 15:53
  • @ZainUlAbidenIftikhar Also, with your call to the destructor explicitly, what happens if the call to `new[]` later on fails? You now have an object that is totally unusable. If you were to write this correctly, you first allocate, then you copy, then you destroy. That's the order that things should be done in, and there is no way to keep that order with the code you have now that explicitly calls the destructor. – PaulMcKenzie Dec 26 '21 at 16:09

2 Answers2

2

There are 2 problems with your code.

Mistake 1: You have not included iostream in MyString.h

Solution to mistake 1

MyString.h

#pragma once
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream> //ADDED THIS

class MyString
{
    char* str; 
    int length;
public:
    MyString();
    MyString(const char*);
    ~MyString();
    MyString (const MyString &Obj);
    void Display() const;
    int GetLength() const;
    const MyString& operator = (const MyString&);
    friend std::ostream& operator << (std::ostream &output,MyString&s); //ADDED STD::  here
    friend void doSomething();
};
#endif

Mistake 2: You have a missing & symbol while defining operator<< in main.cpp.

Solution to Mistake 2

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include "MyString.h"
#include <iostream>
using namespace std;


ostream &operator << (ostream& output, MyString &s) {//ADDED & HERE
        
        output << s.length;
        return output;
}
void doSomething() {
    MyString s;
    s.length;
}

int main() {

    return 0;
}

Additionally, i have added #inlcude <cstring> in MyString.cpp. So MyString.cpp now looks like:

MyString.cpp

#define _CRT_SECURE_NO_WARNINGS
#include "MyString.h"
#include <iostream>
#include <cstring>
using namespace std;

MyString::MyString() {
    length = 0;
    str = new char[1];
    str[length] = '\0';
}

MyString::MyString(const char* cString) {
    length=strlen(cString);
    str = new char[length+1];
    strcpy(str,cString);
    str[length] = '\0';
}

MyString::~MyString() {
    delete[] str;
    str = nullptr;
    length = 0;
}

MyString::MyString(const MyString& Obj) {
    
    length = Obj.length;
    str = new char[length+1];
    strcpy(str, Obj.str);
    str[length] = '\0';
}

void MyString::Display() const {
    cout << str << endl;
}

int MyString::GetLength() const  {
    return length;
}

const MyString& MyString::operator = (const MyString& Obj) {
    this->~MyString();
    length = Obj.length;
    str = new char[length + 1];
    strcpy(str, Obj.str);
    return *this;
}

The program now works(compiles) as can be verified here.

Additional Problem

You should not call the destructor(the way you're calling on the current object) from inside operator='s body.

You can solve this by using @PaulMcKenzie's suggestion in the comments of your original question.

const MyString& MyString::operator = (const MyString& Obj)
{  MyString temp(Obj); 
   std::swap(temp.str, str); 
   std::swap(temp.length, length); 
  
   return *this;
}
Jason
  • 36,170
  • 5
  • 26
  • 60
  • There are still problems with this code, such as explicitly calling the destructor for `MyString` in the `operator =` implementation. – PaulMcKenzie Dec 26 '21 at 15:56
  • @paulMckenzie , In order to copy a new value to the string of different size as the previous one we have to allocate a new memory size and deallocate previous, that is why I called destructor. – Zain Ul Abiden Iftikhar Dec 26 '21 at 16:01
  • @ZainUlAbidenIftikhar -- In order to stop my car, instead of using the brakes, I cut a hole in the floor and stick my foot on the ground. That's basically what you're saying. The proper way to do this is to do what I mentioned in my comment in the main section. Those 3 lines of code I posted magically does everything you say you want to happen, all without the smell of calling destructors explicitly. [See this is as to why my method works correctly](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom). – PaulMcKenzie Dec 26 '21 at 16:02
0

your header file doesn't include and uses a "custom" type ostream, rather than std::ostream. In order to fix it, your header file should be

#pragma once
#include <iostream>
class MyString
{
    char* str; 
    int length;
public:
    MyString();
    MyString(const char*);
    ~MyString();
    MyString (const MyString &Obj);
    void Display() const;
    int GetLength() const;
    const MyString& operator = (const MyString&);
    friend std::ostream& operator << (std::ostream &output,MyString&s);
};

it's a result of using "using namespace std;" in you cpp file : the compiler allows you to not write std::ostream after it has read it. It will auto complete it. But as the type is not defined in your .h file and you don't refet to std::ostream, it will overload the function rather than using the "friend" one

laenNoCode
  • 244
  • 2
  • 8