Thread: Member function from class as a friend in another class

  1. #1
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193

    Question Member function as a friend in another class

    Good afternoon. A Happy New Year, I wish you. (Yoda's style).

    I'm trying to do this:

    Code:
     
    class Screen {
        // Window_mgr::clear must have been declared before class Screen
        friend void Window_mgr::clear(ScreenIndex);
        // ... rest of the Screen class
    };
    But Screen won't be recognized if I put the WindowMgr declaration above that class. So for now, the code is structured like this:

    Code:
    #ifndef SCREEN00_H_ 
    #define SCREEN00_H_ 
    
    #include <string> 
    #include <vector>
    
    using std::string;
    using std::vector;
    
    class Screen 
    { 
        friend class WindowMgr;
        public: 
          typedef string::size_type pos; 
          Screen() = default;
          Screen(pos ht = 0, pos wd = 0) : cursor(0), height(ht), 
          width(wd), contents(ht * wd, ' ') {}
          Screen(pos ht, pos wd, char c) : height(ht), width(wd), 
          contents(ht * wd, c) {}
          char get() const { return contents[cursor]; }
          inline char get(pos r, pos c) const
          { 
             pos row = r * width; 
             return contents[row + c];      
          }       
          inline Screen& move(pos r, pos c)
          { 
            pos row = r * width; // compute the row location 
            cursor = row + c;    // move cursor to the column within that row
            return *this;         // return this object as an lvalue  
          }           
          inline void someMember() const { ++access_ctr; }       
          inline Screen& set(char c)
          { 
             contents[cursor] = c; 
             return *this;   
          }       
          inline Screen& set(pos r, pos col, char ch)
          { 
             contents[r*width + col] = ch;
             return *this;  
          }
          Screen& display(std::ostream& os) 
          { 
             do_display(os);
             return *this;       
          }                  
          const Screen& display(std::ostream& os) const 
          { 
             do_display(os); 
             return *this;  
          }       
        private: 
          mutable size_t access_ctr;
          pos cursor = 0;
          pos height = 0, width = 0; 
          std::string contents;
          void do_display(std::ostream& os) const 
          { 
            os << contents;  
          }       
    };
    
    class WindowMgr 
    { 
       public: 
          using ScreenIndex = std::vector<Screen>::size_type;
          void clear(ScreenIndex);
       private: 
          std::vector<Screen> screens{Screen(24, 80, ' ') };    
    };
    #endif
    Code:
     
    #include "Headers/screen00.h"
    #include <string> 
    
    using std::string;
    
    void WindowMgr::clear(ScreenIndex i)
    {
        // s is a reference to the Screen we want to clear
        Screen &s = screens[i];
        // reset the contents of that Screen to all blanks
        s.contents = string(s.height * s.width, ' ');
    }
    Code:
    #include "Headers/screen00.h"
    #include <iostream> 
    
    using std::cout; 
    using std::endl;
    
    int main() 
    { 
       Screen myScreen(5,3);
       // move the cursor to a given position, and set that character 
       myScreen.move(4,0).set('#');
       
       Screen nextScreen(5, 5, 'X');
       nextScreen.move(4,0).set('#').display(cout);
       cout << "\n";
       nextScreen.display(cout);
       cout << endl;
    
       const Screen blank(5, 3);
       myScreen.set('#').display(cout);  // calls nonconst version
       cout << endl;
       blank.display(cout);              // calls const version
       cout << endl;
       
       return EXIT_SUCCESS;
    }
    Last edited by thames; 01-01-2013 at 12:23 PM. Reason: added the other two src

  2. #2
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Did you try a foreward declaration?

    //Happy new year thames, with health first of all
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  3. #3
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    What do you mean, put WindowMgr before Screen? yes, I did. However, Screen is not recognized as a type inside vector.

  4. #4
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Not the whole class, only a declaration, a forward declaration.

    Example.
    Code:
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    
    class A; //forward declaration
    
    class B{
    private:
        A* objA; // why is this a pointer?
        //Because type A is still incomplete
    public:
        void printB() {cout<<"okFromB"<<endl;}
    };
    
    class A{
    private:
        B objB;
    public:
        void printA() {cout<<"okFromA"<<endl;}
    };
    
    int main(int argc, char** argv) {
    
        A exA;
        exA.printA();
        
        
        B exB;
        exB.printB();
        
        cout<<"End of main"<<endl;
        return 0;
    }
    I do really know if this way of thinking can help you, but I this what came first in my mind
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  5. #5
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    You cannot put your WindowMgr "above that class", e.g., include its header file before including Screen's header file, because you attempt to use an incomplete type (Screen) when initialising your vector:

    Code:
    class Screen;
    
    class WindowMgr
    {
       public:
          using ScreenIndex = std::vector<Screen>::size_type;
          void clear(ScreenIndex);
       private:
          std::vector<Screen> screens{Screen(24, 80, ' ') }; // Screen is an incomplete type.
    };
    You should move your initialisation of `screens` to the .cpp file, because the class Screen must be defined prior to that initialisation.
    In header files, you should generally avoid complex initialisation/definition, especially when it requires inclusion of additional headers, and which can be moved to a .cpp file.
    Last edited by kmdv; 01-01-2013 at 03:52 PM.

  6. #6
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    Code:
     because you attempt to use an incomplete type
    What's an incomplete type?

    I wrote the header like this now (just putting the forward declaration before WindowMgr):

    [edit] I didn't move screens because the authors defined it that way. [/edit]

    Code:
    #ifndef SCREEN00_H_ 
    #define SCREEN00_H_ 
    
    #include <string> 
    #include <vector>
    
    using std::string;
    using std::vector;
    
    class Screen 
    { 
        // friend class WindowMgr;
        friend void WindowMgr::clear(ScreenIndex);
        public: 
          typedef string::size_type pos; 
          Screen() = default;
          Screen(pos ht = 0, pos wd = 0) : cursor(0), height(ht), 
          width(wd), contents(ht * wd, ' ') {}
          Screen(pos ht, pos wd, char c) : height(ht), width(wd), 
          contents(ht * wd, c) {}
          char get() const { return contents[cursor]; }
          inline char get(pos r, pos c) const
          { 
             pos row = r * width; 
             return contents[row + c];      
          }       
          inline Screen& move(pos r, pos c)
          { 
            pos row = r * width; // compute the row location 
            cursor = row + c;    // move cursor to the column within that row
            return *this;         // return this object as an lvalue  
          }           
          inline void someMember() const { ++access_ctr; }       
          inline Screen& set(char c)
          { 
             contents[cursor] = c; 
             return *this;   
          }       
          inline Screen& set(pos r, pos col, char ch)
          { 
             contents[r*width + col] = ch;
             return *this;  
          }
          Screen& display(std::ostream& os) 
          { 
             do_display(os);
             return *this;       
          }                  
          const Screen& display(std::ostream& os) const 
          { 
             do_display(os); 
             return *this;  
          }       
          pos size() const  {return height * width;}
        private: 
          mutable size_t access_ctr;
          pos cursor = 0;
          pos height = 0, width = 0; 
          std::string contents;
          void do_display(std::ostream& os) const 
          { 
            os << contents;  
          }       
    };
    
    class Screen;
    
    class WindowMgr 
    { 
       public: 
          using ScreenIndex = std::vector<Screen>::size_type;
          ScreenIndex addScreen(const Screen& );
          void clear(ScreenIndex);
       private: 
          vector<Screen> screens{Screen(24, 80, ' ') };    
    };
    #endif
    Last edited by thames; 01-01-2013 at 04:13 PM.

  7. #7
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    >What's an incomplete type?
    When you declare a variable of type X, then a variable of size of X is created. When the type is incomplete, the variable just can not be created. That's why I used a pointer in my example. The pointer, regardless where it points too, will have the same size.

    Incomplete types can occur in the forward declaration. You, the programmer, say to the compiler that I will define a type with name A (see my example), but for now I am just declaring it (I just give you the name "class A"), because I need to use the name of the type. But with some restrictions, like the ones that the user from Poland correctly stated out. You can use it for a pointer, but not for a real object.

    In other words, the incomplete type has to do with a type that is not yet defined (it's fields are yet undefined).

    In my example, the fields of A are defined only after the definition of class B. Until the definition of class B, A's size, fields and members are undefined. The compiler just knows that there is a class A, that will be defined later (you see, the compiler trusts you...(almost ;p ))
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  8. #8
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Incomplete types

    Quote Originally Posted by thames View Post
    I didn't move screens because the authors defined it that way.
    And? Is there any problem in moving its initialisation to .cpp?

    You must either move that initialisation to the .cpp file, or leave the definition of WindowMgr "below the definition of Screen". I recommend the former.

  9. #9
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    I modified WindowMgr including two constructors with member initialization lists and a Set function for screens:

    Code:
    #ifndef SCREEN00_H_ 
    #define SCREEN00_H_ 
    
    #include <string> 
    #include <vector>
    
    using std::string;
    using std::vector;
    
    class Screen 
    { 
        // friend class WindowMgr;
        friend void WindowMgr::clear(ScreenIndex);
        public: 
          typedef string::size_type pos; 
          Screen() = default;
          Screen(pos ht = 0, pos wd = 0) : cursor(0), height(ht), 
          width(wd), contents(ht * wd, ' ') {}
          Screen(pos ht, pos wd, char c) : height(ht), width(wd), 
          contents(ht * wd, c) {}
          char get() const { return contents[cursor]; }
          inline char get(pos r, pos c) const
          { 
             pos row = r * width; 
             return contents[row + c];      
          }       
          inline Screen& move(pos r, pos c)
          { 
            pos row = r * width; // compute the row location 
            cursor = row + c;    // move cursor to the column within that row
            return *this;         // return this object as an lvalue  
          }           
          inline void someMember() const { ++access_ctr; }       
          inline Screen& set(char c)
          { 
             contents[cursor] = c; 
             return *this;   
          }       
          inline Screen& set(pos r, pos col, char ch)
          { 
             contents[r*width + col] = ch;
             return *this;  
          }
          Screen& display(std::ostream& os) 
          { 
             do_display(os);
             return *this;       
          }                  
          const Screen& display(std::ostream& os) const 
          { 
             do_display(os); 
             return *this;  
          }       
          pos size() const  {return height * width;}
        private: 
          mutable size_t access_ctr;
          pos cursor = 0;
          pos height = 0, width = 0; 
          std::string contents;
          void do_display(std::ostream& os) const 
          { 
            os << contents;  
          }       
    };
    
    class Screen;
    
    class WindowMgr 
    { 
       public: 
          WindowMgr() = default;
          WindowMgr(const vector<Screen> scrs) : screens(scrs) {}
          using ScreenIndex = std::vector<Screen>::size_type;
          ScreenIndex addScreen(const Screen& );
          void clear(ScreenIndex);
          void SetVector(const vector<Screen> scrs) : screens(scrs) {}
       private: 
          vector<Screen> screens;    
    };
    #endif
    Code:
    #include "Headers/screen00.h"
    #include <string> 
    
    using std::string;
    
    void WindowMgr::clear(ScreenIndex i)
    {
        // s is a reference to the Screen we want to clear
        Screen &s = screens[i];
        // reset the contents of that Screen to all blanks
        s.contents = string(s.height * s.width, ' ');
    }
    
    WindowMgr::ScreenIndex WindowMgr::addScreen(const Screen& s) 
    { 
       screens.push_back(s); 
       return screens.size() - 1;    
    }
    Code:
    #include "Headers/screen00.h"
    #include <iostream> 
    #include <vector>
    
    using std::cout; 
    using std::endl;
    using std::vector;
    
    int main() 
    { 
       vector<Screen> v{Screen(24, 80, ' ')};
       WindowMgr wm(v);
       Screen myScreen(5,3);
       // move the cursor to a given position, and set that character 
       myScreen.move(4,0).set('#');
       Screen nextScreen(5, 5, 'X');
       nextScreen.move(4,0).set('#').display(cout);
       cout << "\n";
       nextScreen.display(cout);
       cout << endl;
    
       const Screen blank(5, 3);
       myScreen.set('#').display(cout);  // calls nonconst version
       cout << endl;
       blank.display(cout);              // calls const version
       cout << endl;
       
       return EXIT_SUCCESS;
    }
    however, my first two errors are:

    Code:
    thames@semaht ~/C++/Projects/Screen $ g++ -g -Wall screen00.cc main_screen00.cc -std=c++11
    In file included from screen00.cc:1:0:
    Headers/screen00.h:13:14: error: ‘WindowMgr’ undefined
    Headers/screen00.h:13:31: error: ‘ScreenIndex’ undefined

  10. #10
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Change:
    Code:
    // friend class WindowMgr;
    friend void WindowMgr::clear(ScreenIndex);
    To:
    Code:
    friend class WindowMgr;
    // friend void WindowMgr::clear(ScreenIndex);
    The class WindowMgr is not yet defined and the compiler does not know what member functions it has. Yet there is no reason to make a friend only one member function of a specified class. Make a friend the whole WindowMgr.

  11. #11
    Tears of the stars thames's Avatar
    Join Date
    Oct 2012
    Location
    Rio, Brazil
    Posts
    193
    Code:
     Make a friend the whole WindowMgr
    Can this lead to further problems?
    Last edited by thames; 01-01-2013 at 06:57 PM.

  12. #12
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    That depends on your design and, specifically, whether folks maintaining the WindowMgr class can be relied on not to accidentally mess up the state of Screen objects. Not a big issue for a hobby program, but if you expect the classes to be maintained by a team of 50 people over time, it becomes a significant consideration.

    If only one member function of a class, rather than the whole class needs access to internals in another then it is appropriate to make only the member function a friend.

    That said, I'd probably provide a public member function that controls access to the private members, rather than making another class or its member functions friends.


    If you really want a member function to be a friend, it is necessary for the class which owns the member to be fully defined (not just a forward declaration). So, in this case, the compiler needs to see the complete definition of the WindowMgr class before it sees the declaration of WindowMgr(ScreenIndex) as a friend.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  13. #13
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    In general it is not good to have friend classes. I would suggest to use them for overloading operators (output and input for example).. So, do this, only if you have no other solution..
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  14. #14
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Quote Originally Posted by std10093 View Post
    In general it is not good to have friend classes.
    On what grounds? It does not violate encapsulation. Use of "friend" can be justified, if you need to grant access to a class or function. Friend classes can be justified if they are meant to be coupled (any changes to one class implies changes to another class as well).

  15. #15
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Quote Originally Posted by whiteflags View Post
    On what grounds?
    I have in mind what grumpy said its 1st paragraph. I suppose that we should design our code, like if it was to be handled by 100 programmers, even though it may be just for fun. That way, when the time comes for big projects, you are going to be prepared
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Issue with namespace function declared as friend of class
    By Programmer_P in forum C++ Programming
    Replies: 7
    Last Post: 09-22-2012, 12:46 PM
  2. Replies: 4
    Last Post: 03-22-2012, 01:11 PM
  3. friend function template class declaration error
    By -EquinoX- in forum C++ Programming
    Replies: 2
    Last Post: 11-20-2009, 01:00 PM
  4. Replies: 8
    Last Post: 03-19-2008, 03:04 AM
  5. Replies: 5
    Last Post: 06-04-2002, 02:33 PM

Tags for this Thread