1

I've got a problem with friend function between two classes. Lets see some code:

First class:

#ifndef _FIRST_H_
#define _FIRST_H_

//#include "Second.h"
#include <string>

class Second;
class First
{
    friend void Second::fun();

    std::string str = "Dziala\n";
public:
    First();
    ~First();
};
#endif

and Second class:

#ifndef _SECOND_H_
#define _SECOND_H_

#include<iostream>
#include "First.h"

class Second
{
    First fObj;
public:
    Second();
    ~Second();
    void fun() { std::cout << fObj.str; }
};
#endif 

There is no problem if I try to make friend CLASS. The problem occures if I make friend FUNCTION like in the above example. I can fix this by #include "Second.h" in First class, but then it will be include loop. Do you have any idea how to do this?

QueUe
  • 91
  • 4
  • 7

3 Answers3

2

The problem occures if I make friend FUNCTION like in the above example.

// #include "Second.h"
#include <string>

class Second;
class First
{
    friend void Second::fun();
...

The first line is a declaration of class Second. It is a forward declaration. For class Second, after its declaration and before its definition is seen, it is incomplete type. So Second is known as class type, but the members it contains is unknown. So you cannot use the member void Second::fun() here.

friend class Second works fine, as it never try to use a member of a incomplete type.

but then it will be include loop.

As MadsMarquart said, it's not a problem since you already have header guard.

any idea how to do this?

If you want to use friend void Second::fun() as a friend declaration, the order of declaration and definition is important and a little modification to your class is necessary.

  1. Declare class First.
  2. Define class Second with the declaration (not definition) of fun().
    • You cannot use a First fObj as member or try to new First right now, since the First is not defined and the constructor of First is unknown right now. A pointer or reference would be fine.
    • Since pointer or reference is used, the **constructor should be also modified.
  3. Define class First with the friend declaration of fun().
  4. Define fun().

Codes modified base on your example,

class First;

class Second {
 public:
  Second(First& rfObj) : fObj(rfObj) {}
  void fun();

 private:
  First& fObj;
};

class First {
  friend void Second::fun();
 public:
  First() = default;

private:
  std::string str = "Dziala\n";
};

void Second::fun() { std::cout << fObj.str; }
Chao Mai
  • 21
  • 1
  • 7
0

Do you have any idea how to do this?

You cannot use the friend mechanism to do what you are trying.

A simple solution is to expose First::str through a member function and not worry about the friend construct. That is a cleaner solution by a long shot.

class First
{
  public:
    First();
    ~First();

    std::string const& getString() const { return str; }

  private:
    std::string str = "Dziala\n";
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Why? If I can use friend CLASS, so why I cannot use friend FUNCTION mechanism? – QueUe Nov 06 '15 at 22:09
  • 1
    @QueUe, in my experience, using the `friend` mechanism is the last resort -- doesn't matter whether you are making a class or a function a `friend`. It is a symptom of very tight coupling between classes. By making `Second` a friend of `First`, you have made `Second` tightly coupled with the internals of `First`. `First` cannot change its internals freely without consulting `Second`. – R Sahu Nov 06 '15 at 22:27
  • Unfortunately, It's the homework on my programming course. I have to use friend function :) – QueUe Nov 06 '15 at 22:35
  • @QueUe, are you required to make a member function a `friend` as part of your assignment? You know that you simply cannot do what you are trying. Read more carefully what the assignment is trying to accomplish and you might find the solution. – R Sahu Nov 06 '15 at 22:37
  • Yes, it is required. It's strange because I am able to use friend class here but not friend function. Btw, thank you :) – QueUe Nov 06 '15 at 22:41
0

It is not possible to declare a member function as a friend unless the complete class definition is visible. Allowing otherwise would permit arbitrary code to declare and define members of a class that the creator of that class does not intend.

class Second     // complete class definition, not just a forward declaration
{
    public:

       void fun();
};

class First
{
    friend void Second::fun();
};

A consequence of this is that First cannot simply be a member of Second. To accept that, the compiler needs to have visibility of the complete definition of Second for the definition of First to compile, but also have visibility of the complete definition of First for the definition of Second to compile. That is an infinitely recursive dependence, which tends to upset compilers.

The only types of class members (or variables in general actually) that can be declared with only a forward declaration are pointers or references.

So, this would work

class First;

class Second     // complete class definition, not just a forward declaration
{
    private:
        First &fObj;    // note this is a reference
    public:

       void fun();

       Second();
       ~Second();
};

class First
{
    friend void Second::fun();
};

Second::Second() : fObj(*(new First))   // assumes First has appropriate (not shown) constructor
{}

Second::~Second()
{
   delete &fObj;
}

However, note that both the constructor and destructor of Second also cannot be compiled unless the definition of First is visible to the compiler beforehand. This is because it is not possible to create or destroy an instance of a class type based on only a forward declaration (i.e. the same reason as for your original problem).

Practically, I'd just declare class Second to be a friend of First and be done with it. After all, declaring one member function of a class as a friend asserts that member function will always be implemented to work as intended. There are very few circumstances in which it is possible to trust a single member function of a class to work as required, but not trust other member functions of the same class.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • It appears a series of edits were proposed and rejected by other members for my answer in the last 24 hours. My last edits were actually proposed by Chao Mai to correct a couple of errors, because for some reason I had no discretion to accept (as I would have) or reject them. – Peter Feb 09 '16 at 10:24