Keword "friend" unneeded required.

2004-09-19
2012-09-26
  • SoaringFalcon

    SoaringFalcon - 2004-09-19

    The "friend" keyword is used to access private/protected members. I know that. But DevCpp 4990 requires me to add the friend keyword here also (see code below) even when members are public:

    #ifndef PERSON_H
    #define PERSON_H

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

    class Person
    {
    public:
        string fname;
        string lname;
        string tele;
        friend ostream& operator<<(ostream& os, const Person& p);
        friend istream& operator>>(istream& is, Person& p);
    };

    #endif //PERSON_H

    //Person.cpp
    #include "Person.h"

    ostream& operator<<(ostream& os, const Person& p)
    {
        os <<p.fname<<" "<<p.lname<<" "<<p.tele;
        return os;
    }

    istream& operator>>(istream& is, Person& p)
    {
        is >>p.fname
            >>p.lname
            >>p.tele;
        return is;
    }

    //end Person.cpp

    And here is how it is used (super simple):

    #ifndef PHONEBOOK_H
    #define PHONEBOOK_H

    #include "Person.h"
    #include <vector>
    #include <iterator>
    #include <iostream>
    #include <algorithm>
    #include <fstream>
    using namespace std;

    class PhoneBook
    {
    public:   
        PhoneBook();
        vector<Person>& getPhoneBook();
    private:
        bool getFile();
        vector<Person> PhoneBk;
    };

    #endif //PHONEBOOK_H

    //PhoneBook.cpp
    #include "PhoneBook.h"

    PhoneBook::PhoneBook() { getFile(); }

    bool PhoneBook::getFile()
    {
       ifstream InFile("phonebook.txt");
       if(!InFile)
          return false;
       Person temp;
       //Uses overloaded extractor.
       while(InFile>>temp)
          PhoneBk.push_back(temp);
       //If the while loop exits before eof is reached, something has gone wrong.
       if( !InFile.eof() )
       return false;
       InFile.close();
       return true;
    }
       
    vector<Person>& PhoneBook::getPhoneBook() { return PhoneBk; }
    //end PhoneBook.cpp

    If I remove the frriend tags then this is the log:
    Compiler: Default compiler
    Building Makefile: "D:\Dev-Cpp\MyProjects\VectorSortTest\Makefile.win"
    Executing  make clean
    rm -f Main.o PhoneBookEngine.o PhoneBook.o Person.o ConsoleView.o  ViewSeperation.exe

    g++.exe -D__DEBUG__ -c Main.cpp -o Main.o -I"D:/Dev-Cpp/include/c++/3.3.1"  -I"D:/Dev-Cpp/include/c++/3.3.1/mingw32"  -I"D:/Dev-Cpp/include/c++/3.3.1/backward"  -I"D:/Dev-Cpp/lib/gcc-lib/mingw32/3.3.1/include"  -I"D:/Dev-Cpp/include"    -fexceptions -g3

    g++.exe -D__DEBUG__ -c PhoneBookEngine.cpp -o PhoneBookEngine.o -I"D:/Dev-Cpp/include/c++/3.3.1"  -I"D:/Dev-Cpp/include/c++/3.3.1/mingw32"  -I"D:/Dev-Cpp/include/c++/3.3.1/backward"  -I"D:/Dev-Cpp/lib/gcc-lib/mingw32/3.3.1/include"  -I"D:/Dev-Cpp/include"    -fexceptions -g3

    g++.exe -D__DEBUG__ -c PhoneBook.cpp -o PhoneBook.o -I"D:/Dev-Cpp/include/c++/3.3.1"  -I"D:/Dev-Cpp/include/c++/3.3.1/mingw32"  -I"D:/Dev-Cpp/include/c++/3.3.1/backward"  -I"D:/Dev-Cpp/lib/gcc-lib/mingw32/3.3.1/include"  -I"D:/Dev-Cpp/include"    -fexceptions -g3

    PhoneBook.cpp: In member function `bool PhoneBook::getFile()':

    PhoneBook.cpp:12: error: no match for 'operator>>' in 'InFile >> temp'
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:83: error: candidates are:
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(std::basic_istream<_CharT,
       _Traits>&(*)(std::basic_istream<_CharT, _Traits>&)) [with _CharT = char,
       _Traits = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:92: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(std::basic_ios<_CharT,
       _Traits>&(*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits
       = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:101: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(std::ios_base&(*)(std::ios_base&)) [with _CharT = char,
       _Traits = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:110: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(bool&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:137: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(short int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:174: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(short unsigned int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:201: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:238: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(unsigned int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:265: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(long int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:292: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(long unsigned int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:320: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(long long int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:347: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(long long unsigned int&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:375: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(float&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:402: error:                

       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(double&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:429: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(long double&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:456: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(void*&) [with _CharT = char, _Traits =
       std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/bits/istream.tcc:483: error:                
       std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT,
       _Traits>::operator>>(std::basic_streambuf<_CharT, _Traits>*) [with _CharT =
       char, _Traits = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/istream:644: error:                
       std::basic_istream<char, _Traits>& std::operator>>(std::basic_istream<char,
       _Traits>&, unsigned char&) [with _Traits = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/istream:649: error:                
       std::basic_istream<char, _Traits>& std::operator>>(std::basic_istream<char,
       _Traits>&, signed char&) [with _Traits = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/istream:685: error:                
       std::basic_istream<char, _Traits>& std::operator>>(std::basic_istream<char,
       _Traits>&, unsigned char*) [with _Traits = std::char_traits<char>]
    D:/Dev-Cpp/include/c++/3.3.1/istream:690: error:                
       std::basic_istream<char, _Traits>& std::operator>>(std::basic_istream<char,
       _Traits>&, signed char*) [with _Traits = std::char_traits<char>]

    make.exe: *** [PhoneBook.o] Error 1

    Execution terminated

     
    • Nobody/Anonymous

      They usually say that when they do not want to put out. Those kinda girls you should stay away from anyways.

      Kip

       
      • Nobody/Anonymous

        What would you know about girls?

         
        • Nobody/Anonymous

          I knew that would summon the phantom-peter. His glasses never rest.

          Kip

           
          • Nobody/Anonymous

            he is just another spectacled retard. just ignore him. everybody hates nerds with glasses that think they know everything

             
            • Kip

              Kip - 2004-09-19

              Amen.

              Kip

               
            • Nobody/Anonymous

              I do know that you are sucking up to Kippy! What will be your reward?

               
              • Nobody/Anonymous

                grabbing you by the glasses and slamming you into a brick wall

                 
                • Kip

                  Kip - 2004-09-20

                  Ha ha! Boo hoo phantom-peter! Count me in too!

                  Kip

                   
    • Nobody/Anonymous

      put

      ostream& operator<<(ostream& os, const Person& p);

      and

      istream& operator>>(istream& is, Person& p);

      outside of the class declaration but leave it in that header. they should not be part of the class they just operate on the class.

      you get the error because there is no >> operator for person in scope at the time it is called.

      obag

       
    • SoaringFalcon

      SoaringFalcon - 2004-09-19

      In wich header do I need to put them then?
      Surely not in every header that uses them?

       
      • Nobody/Anonymous

        you still put them in the PERSON_H header and include it whereever you need access to person.

        obag

         
    • SoaringFalcon

      SoaringFalcon - 2004-09-19

      Thank you obag.

       
    • aditsu

      aditsu - 2004-09-20

      the problem is that you can't declare that operator inside the class
      if you declare operator<< in the class X, it assumes it will first take an X argument (*this), but in your case the first argument is a stream and only the second one is an object of your class

      using "friend" practically takes that operator outside of the class, so it becomes a global function; this way you can specify the types of both arguments
      like obag said, you might as well declare it as a normal global function outside the class

      however, I personally find that keeping it as a friend in the class looks good (all the functions/operators related to the class are kept in the class declaration) and should have no bad side-effect

      Adrian

       
      • Nobody/Anonymous

        generally speaking i hate seeing friend anywhere because of its high level of misuse, i.e. subverting good class design on the user end. i agree that having it as friend does not actually hurt anything, but by the same token why list it as friend if it does not need access to private/protected members? if the declaration is still in the header that is specific to a class or series of related classes then the relationship should still be obvious.

        i am serious in my question.

        just because you like it that way is fine, but i am curious of other reasons.

        obag

         
        • aditsu

          aditsu - 2004-09-20

          > why list it as friend if it does not need access to private/protected members?

          besides the fact that I like it that way (which is due to "visual grouping"), I can only see one other reason:
          it is likely that this kind of operator may need access to private data in a future version of the class

          I agree that these are not strong arguments, and I see no problem in removing "friend" when it's not needed

          of course, when "friend" is misused due to bad design (in whatever part of the project), I think it must be eliminated (and the design must be fixed)

          Adrian

           
        • aditsu

          aditsu - 2004-09-20

          oh, I just remembered another reason: the syntax is much simpler if you have a template class (and possibly more templates involved)

          Adrian

           
          • Nobody/Anonymous

            fair point.

            i am not allowed to inline class template code or for that matter even leave it in the same file. has to be split apart for the unlikely eventuality that our compilers support the export keyword.

            similar to how we have to typedef everything in case we need to change the name of something.

            fortunately for me i spend most of my time designing objects. other people have to implement most of them if they are to be implemented.

            obag

             
    • SoaringFalcon

      SoaringFalcon - 2004-09-20

      I think you guys missed the scope of the problem in my case. Look again at the code I posted.

      - If I don't declare the overloaded operators as a friend,  then class PhoneBook won't see any overloaded extractors (>>) and therefore will error.
      Thre reason is that currently the overloads are only visible at filescope when defined in Person.cpp as global functions.  Therefore, class PhoneBook won't be able use these overloads. We can solve this problem by removing the overloads and placing them on top of the PhoneBook class *definition* in PhoneBook.cpp file. This works.
      But... what if the overloaded inserters and extractors are needed by other classes in other files?
      Copying a 2nd version of the overloads to that (new) source file will generate a "mulitiple redefinition" error  by the compiler.

      So just adding the overloaded extractors to the files is not the sulution. UNLESS I make it a "friend" of the class Person and keep only one definition in the Person.cpp file. Now my overloads are recognized by all files. This seems to be a 'hack' however. In theory, the overloads remain only at filescope.

      So my question once again, where do I define my global overloads and measures do I need to take, other than adding a namespace (to indetify my version of the overload)?

      Also note that extraction/insertion overloads may be defined as a class member. But this makes the overload not really handy in use. Therefore these extractors *kind of should* not be defined as members. But that's totally not the issue right now.

       
      • SoaringFalcon

        SoaringFalcon - 2004-09-20

        I need to add one solution though. But it might be critised:

        I could keep the definition in a header or sourcefile (not important right now), include that header in any file that  uses these overloads and declare them "external" upfront.
        Below a sample:

        //Begin MyOverloads.h
        #include Person.h

        ostream& operator<<(ostream& os, const Person& p)
        {
            os <<p.fname<<" "<<p.lname<<" "<<p.tele;
            return os;
        }

        istream& operator>>(istream& is, Person& p)
        {
            is >>p.fname
                >>p.lname
                >>p.tele;
            return is;
        }

        //End MyOverloads.h

        Nevermind namespaces and multiple inclusion problems for now.

        Then before each file uses my overloads add this:

        //PhoneBook.cpp
        extern ostream& operator<<(ostream& os, const Person& p);
        extern istream& operator>>(istream& is, Person& p);

        Person temp;
        while(!InFile >> temp)
        { ... }
        //End PhoneBook.cpp

        Etcetera.
        But, the (in)famous "Herbert Schildt" wrote that extern for functions is "depreciated". But he didn't write why, and didn't present this implied-so-called new way of doing things.
        Does anyone know what's up with that?
        I have to say I am not a fan of this lad, but I always am prepared to listen.

        Shoot... :)

         
    • Nobody/Anonymous

      okay, just wow. you totally misunderstood what i said. read it again. you also seem to misunderstand the object of headers in general.

      extern is depcriciated because all functions are extern by default.

      obag

       
    • SoaringFalcon

      SoaringFalcon - 2004-09-21

      If this was your answer (lot of "nobodies" in here" )...

      > put

      > ostream& operator<<(ostream& os, const Person& p);

      > and

      > istream& operator>>(istream& is, Person& p);

      > outside of the class declaration but leave it in that header.

      ... then I didn't understood what you meant :)
      You meant to leave the DEFINITION where it is and to FORWARD DECLARE them in the headers that use them. In that case, that works, and at the same time demonstrates how the keyword "extern" is not needed anymore for functions.

       
    • aditsu

      aditsu - 2004-09-22

      ok, just to clarify:
      your original header file was this:

      ifndef PERSON_H

      define PERSON_H

      include <iostream>

      include <string>

      using namespace std;

      class Person
      {
      public:
      string fname;
      string lname;
      string tele;
      friend ostream& operator<<(ostream& os, const Person& p);
      friend istream& operator>>(istream& is, Person& p);
      };

      endif //PERSON_H

      the main point is to change it to this:

      ifndef PERSON_H

      define PERSON_H

      include <iostream>

      include <string>

      using namespace std;

      class Person
      {
      public:
      string fname;
      string lname;
      string tele;
      };

      ostream& operator<<(ostream& os, const Person& p);
      istream& operator>>(istream& is, Person& p);

      endif //PERSON_H

      and leave everything else as is

      alternatively, you can define the operators inline in the header file and remove them from the cpp file:

      inline ostream& operator<<(ostream& os, const Person& p)
      {
      os <<p.fname<<" "<<p.lname<<" "<<p.tele;
      return os;
      }

      (same for >>)

      Adrian

       
    • SoaringFalcon

      SoaringFalcon - 2004-09-22

      Yep yep :) That I knew but I didn't realize that global functions were "explicit" by default so I didn't understand how this could work. Therefore I thought I did it wrong.
      But I also didn't realize that obag (whom help I greatly appreciate) offered the same solution as you and me did. :)

      Anyway, great job everybody.

       

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks