Thread: Class Members as Friends - Beginning Visual C++ 2008

  1. #1
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63

    Question Class Members as Friends - Beginning Visual C++ 2008

    Hi,

    I was working through all of the example code in chapter 9, Class Inheritance and Virtual Functions, when I came upon the Class Members as Friends portion of the chapter. It looked really short and I figured I'd knock it out before heading into the section on Virtual Functions.

    It didn't turn out that way. Long story short, the example code appeared to be broken, so I consulted the 2010 edition of the book (picked up on a good sale) and the example had been expanded to introduce forward declarations to resolve the issue of one class depending on another class. That combined with moving the implementation of the constructor for the second class (which accepts a reference to the first class) into a .cpp file seemed to have done the trick.

    Here's the code: class_mem_as_friends, r23

    I had come across Headers and Includes: Why and How while doing a Google search, and I thought I had everything figured out, but when I change the order of the include statements in any of the .cpp files, all sorts of errors are given.

    So, I guess my question is this:

    Why must CCarton.h be included before CBottle.h ? I feel like I'm overlooking something obvious.

    Reading the previously mentioned "Headers and Includes: Why and How" article, I thought that these two items would apply:

    do nothing if: The only reference to B is in a friend declaration
    I figured that would apply to CBottle.h since the constructor from CCarton.h was made a friend function.

    forward declare B if: one or more functions has a B object/pointer/reference
    as a parementer, or as a return type: B MyFunction(B myb);
    I figured that would apply to CCarton.h since the constructor accepts a reference to a CBottle object.

    I'm stumped.

    Thanks for your time.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by deoren
    Why must CCarton.h be included before CBottle.h ?
    Because the code is broken: by right, the order of includes should not matter, but it does. Unfortunate, but it happens in real life.

    Anyway, the reason is that the friendship declaration does not declare CCarton as a friend of CBottle. Rather, it declares CCarton::CCarton as a friend of CBottle, hence the definition of CCarton must be available at that point. Thus, instead of the forward declaration of CCarton, CBottle.h should #include "CCarton.h".
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Thanks for the reply.

    Quote Originally Posted by laserlight View Post
    Because the code is broken: by right, the order of includes should not matter, but it does. Unfortunate, but it happens in real life.
    Thanks for confirming that. I'll look to other books for better examples of 'Class Members as Friends'.


    Quote Originally Posted by laserlight View Post
    Anyway, the reason is that the friendship declaration does not declare CCarton as a friend of CBottle. Rather, it declares CCarton::CCarton as a friend of CBottle, hence the definition of CCarton must be available at that point. Thus, instead of the forward declaration of CCarton, CBottle.h should #include "CCarton.h".
    So if I'm understanding you right, the CCarton class could be declared a friend of CBottle, and the defintion of CCarton wouldn't be required (forward declaration in place of the #include line?) yet?
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by deoren View Post
    So if I'm understanding you right, the CCarton class could be declared a friend of CBottle, and the defintion of CCarton wouldn't be required (forward declaration in place of the #include line?) yet?
    No, the definition of CCarton is needed because CBottle.h references the constructor, not just the class. So it should look like:

    Code:
    // $Id$
    // $HeadURL$
    
    #pragma once
    
    #include "CCarton.h"
    
    class CBottle
    {
    public:
        CBottle(double height, double diameter);
    
    private:
        double m_Height; // Bottle height
        double m_Diameter; // Bottle diameter
    
        // Let the carton constructor in
        friend CCarton::CCarton(const CBottle& aBottle);
    };
    There is a sort of issue here in so far as using a relative path ("CCarton.h" instead of, eg. <CContainers/CCarton.h>) may complicate things depending on the context in which these are used -- but that is less of an issue than needing to specify the order of the includes correctly.

    Also, it would be better (more standardized) to use include guards instead of pragma once:

    Code:
    #ifndef CBOTTLE
    #define CBOTTLE
    
    #include "CCarton.h"
    
    class CBottle
    {
    public:
        CBottle(double height, double diameter);
    
    private:
        double m_Height; // Bottle height
        double m_Diameter; // Bottle diameter
    
        // Let the carton constructor in
        friend CCarton::CCarton(const CBottle& aBottle);
    };
    
    #endif
    Last edited by MK27; 02-25-2012 at 08:35 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Quote Originally Posted by deoren View Post
    So if I'm understanding you right, the CCarton class could be declared a friend of CBottle, and the defintion of CCarton wouldn't be required (forward declaration in place of the #include line?) yet?
    I think I answered my own question. Not only was broken code provided, but the following section where the syntax for making a class a friend of another class was wrong too:

    Code:
    friend CCarton;
    instead of

    Code:
    friend class CCarton;
    From CBottle.h (r23) I removed the forward declaration, the explicit friend of class declaration for the CCarton constructor and also swapped the order of include statements in class_mem_as_friends.cpp (r23) to test the changes.

    This results in:



    Diff here and below.

    Code:
    Index: CBottle.h
    ===================================================================
    --- CBottle.h	(revision 23)
    +++ CBottle.h	(working copy)
    @@ -3,11 +3,11 @@
     
     #pragma once
     
    -// Forward declaration
    -class CCarton;
    -
     class CBottle
     {
    +
    +friend class CCarton;
    +
     public:
         CBottle(double height, double diameter);
     
    @@ -15,7 +15,5 @@
         double m_Height; // Bottle height
         double m_Diameter; // Bottle diameter
     
    -    // Let the carton constructor in
    -    friend CCarton::CCarton(const CBottle& aBottle);
     };
     
    Index: class_mem_as_friends.cpp
    ===================================================================
    --- class_mem_as_friends.cpp	(revision 23)
    +++ class_mem_as_friends.cpp	(working copy)
    @@ -3,8 +3,8 @@
     
     // Testing CBottle and CCarton classes from pg 528:529
     
    +#include "CBottle.h"
     #include "CCarton.h"
    -#include "CBottle.h"
     
     int main ()
     {
    That compiles fine, no matter the order of include statements in any of the .cpp files.

    My question now:

    Which of those two approaches is the right one? Or is neither better than the other?

    Thanks for your time.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  6. #6
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Quote Originally Posted by MK27 View Post
    No, the definition of CCarton is needed because CBottle.h references the constructor, not just the class.
    Sorry, I just noticed how my reply read. I had originally drafted a reply to laserlight referring to doing that, and wanting to know if either of two other approaches would work, but I scrapped it when I looked further in the book and saw the syntax for making an entire class a friend of another.

    What you're both saying makes some sense to me, but I got hung up on the forward declaration not being enough to satisfy the compiler, particularly with the implementation of the constructors were placed in .cpp files.

    So, pardon the overuse of the phrase, but if I'm understanding both of you right, because the ccarton constructor was explicitly referred to, you'll need to include the CCarton.h file so the compiler can ... verify the friend function is declared properly?

    On the topic of #pragma vs include guards, I had generally been using include guards, but figured I'd go along with what the book was showing. I had gotten curious about whether there were issues with using #pragma once, but Wikipedia had shown that all mainstream compilers properly supported #pragma once now. From what you're saying, it's still not the standardized way of protecting from multiple inclusion?

    If so, I'll stick with the classic approach then.

    Thanks.
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by deoren
    So, pardon the overuse of the phrase, but if I'm understanding both of you right, because the ccarton constructor was explicitly referred to, you'll need to include the CCarton.h file so the compiler can ... verify the friend function is declared properly?
    Yes.

    Quote Originally Posted by deoren
    On the topic of #pragma vs include guards, I had generally been using include guards, but figured I'd go along with what the book was showing. I had gotten curious about whether there were issues with using #pragma once, but Wikipedia had shown that all mainstream compilers properly supported #pragma once now. From what you're saying, it's still not the standardized way of protecting from multiple inclusion?
    There is no standard way. The difference is that header inclusion guards use standard preprocessor directives with standard behaviour, whereas the behaviour of any #pragma directive is inherently compiler specific.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by deoren View Post
    So, pardon the overuse of the phrase, but if I'm understanding both of you right, because the ccarton constructor was explicitly referred to, you'll need to include the CCarton.h file so the compiler can ... verify the friend function is declared properly?
    It has to know that this function actually exists (by being declared somewhere), yeah.

    Which of those two approaches is the right one? Or is neither better than the other?
    There is a difference between your patch and the original, namely, that in the original, only the CCarton constructor has access to the private/protected members of CBottle, whereas with your patch anything in CBottle and any class derived from CBottle have that access.

    Wikipedia had shown that all mainstream compilers properly supported #pragma once now. From what you're saying, it's still not the standardized way of protecting from multiple inclusion?
    It can't be considered standardized because it is not guaranteed by the C++ standard. Include guards are maybe sort of hackish, but they are guaranteed to work. I'll differ to laserlight vis, this means there is no real standard method (because include guards are exploiting something in the standard to accomplish a task the standard does not actually discuss). But pragma once is nicer, so since there is no real standard method, we can always hope that will be adopted eventually (as you say, most compiler writers already have).
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  9. #9
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Quote Originally Posted by laserlight View Post
    There is no standard way. The difference is that header inclusion guards use standard preprocessor directives with standard behaviour, whereas the behaviour of any #pragma directive is inherently compiler specific.
    Quote Originally Posted by MK27 View Post
    It can't be considered standardized because it is not guaranteed by the C++ standard. Include guards are maybe sort of hackish, but they are guaranteed to work.
    Good point. I'll stick with using them unless I find that existing code is already using #pragma once instead of include guards.

    Quote Originally Posted by MK27 View Post
    It has to know that this function actually exists (by being declared somewhere), yeah.
    For now I'm going to have to accept this and just go with it, because my brain keeps telling me that the forward declaration should be enough. That tells me I don't understand the process well enough and will need to do further research on the topic. That way I can finally understand the requirement of including the class header file vs being able to get away with only a forward declaration and have the main code file later do the including.

    Quote Originally Posted by MK27 View Post
    There is a difference between your patch and the original, namely, that in the original, only the CCarton constructor has access to the private/protected members of CBottle, whereas with your patch anything in CBottle and any class derived from CBottle have that access.
    So it's better to limit access where you can, and making a class a friend of another if you only need one function to have access is overkill?

    Quote Originally Posted by MK27 View Post
    anything in CBottle and any class derived from CBottle have that access.
    I didn't think that class friendship could be inherited?

    From C++ Pocket Reference:

    Friendship is not inherited, nor do friends of nested classes have any special access rights to the members of their enclosing class.
    From Ivor Horton's Beginning Visual C++ 2008:

    Class friendship is also not inherited. If you define another class with CBottle as a base, members of the CCarton class will not have access to its data members, not even those inherited from CBottle.
    If class friendship cannot be inherited, does that limit risk considerably and make class friendship a good choice, or should you still explicitly make other class functions friends of the class and include the other class's header file?
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

  10. #10
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by deoren View Post
    I didn't think that class friendship could be inherited?
    Whoops! Correct, I had that backward. This isn't something I've run up against and so I glanced at a reference before saying that, but read the word "except" in "a derived class inherits everything except" as "including". :/ All apologies.

    IMO just making the class a friend is probably better because it's tidier, just thought I'd point out this difference, which may have been intentional. [OTH, Laserlight has some good points here...see below]
    Last edited by MK27; 02-25-2012 at 12:20 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by deoren
    So it's better to limit access where you can, and making a class a friend of another if you only need one function to have access is overkill?
    Yes.

    Quote Originally Posted by deoren
    I didn't think that class friendship could be inherited?
    Yeah, friendship is not inherited.

    Quote Originally Posted by deoren
    If class friendship cannot be inherited, does that limit risk considerably and make class friendship a good choice, or should you still explicitly make other class functions friends of the class and include the other class's header file?
    Friendship increases coupling. Tight coupling is bad because it means that changes to one component will have an greater impact on the other component, thus there is more to consider and more code to change when a change is needed. More changes to the code means more bugs are likely to be written.

    So, if class A is a friend of class B, it means that if you change class B, you need to check class A in its entirety. But if only A's member function foo is a friend of class B, it means that if you change class B, you only need to check A::foo. As such, we can say that friendship reduces encapsulation.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Registered User deoren's Avatar
    Join Date
    Mar 2003
    Posts
    63
    Great points, thank you both!
    It is better to fail with honor than win by deceit
    - unknown

    My erratic tinkerings:
    http://projects.whyaskwhy.org/

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 23
    Last Post: 01-14-2012, 11:11 AM
  2. How do I use MFC in Visual C++ 2008?
    By RaisinToe in forum Windows Programming
    Replies: 15
    Last Post: 04-06-2009, 10:56 AM
  3. Friends and Members
    By MarlonDean in forum C++ Programming
    Replies: 6
    Last Post: 06-18-2008, 11:52 AM
  4. Friends in a template class
    By dalek in forum C++ Programming
    Replies: 2
    Last Post: 09-19-2004, 09:10 PM
  5. friends class lil prblm with enum
    By rip1968 in forum C++ Programming
    Replies: 4
    Last Post: 07-25-2002, 09:57 PM

Tags for this Thread