Legion
Legion

Reputation: 3437

C++ won't let me make friends

I have two classes, Mesh and MeshList. I want MeshList to have a function that can change the private members of Mesh. But it won't compile and I don't know why. Here's my code.

Mesh.h

#ifndef _MESH_H
#define _MESH_H

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

class Mesh
{
private:
    unsigned int vboHandle_;
    friend void MeshList::UpdateVBOHandle();
public:
    inline void Out() {std::cout << vboHandle_;}
};
#endif

Mesh.cpp

#include "Mesh.h"

MeshList.h

#ifndef _MESH_LIST_H
#define _MESH_LIST_H

#include "Mesh.h"


class MeshList
{

public:
    Mesh *mesh; //Line 11 Error
    void UpdateVBOHandle();
};
#endif

MeshList.cpp

#include "MeshList.h"

void MeshList::UpdateVBOHandle()
{
    *mesh->vboHandle_ = 4;
}

I get these errors:

MeshList.h (Line 11)

Upvotes: 32

Views: 1054

Answers (4)

Stephan
Stephan

Reputation: 4247

Cyclic dependencies are explained in the other answers...

Here comes the solution:

In MeshList.h:

  • replace #include "Mesh.h" with the forward declaration class Mesh; (You don't need the include here, because you only declare a pointer to a Mesh)

In MeshList.cpp:

  • add #include "Mesh.h" to your includes (you need the declaration, because you use the Mesh)

The last compile error, you mentioned is another problem:

*mesh->vboHandle_ = 4;

mesh is a pointer. Your code selects the member vboHandle_ and tries to dereference it (which fails). I suppose you mean:

mesh->vboHandle_ = 4; // <-- no leading asterisk

Upvotes: 7

Matthieu M.
Matthieu M.

Reputation: 299880

The problem: Cyclic dependencies in your includes. The error message is less than ideal, unfortunately.


The solution: If you befriend the whole class, instead of a single function, then you can use a forward declaration of the class to break the cycle.

// Mesh.h
#ifndef _MESH_H
#define _MESH_H

#include <iostream>

class MeshList;

class Mesh
{
private:
    unsigned int vboHandle_;
    friend class MeshList;
public:
    inline void Out() {std::cout << vboHandle_;}
};
#endif

Some (subjective) guidelines:

  • Include stuff in reverse order of your ability to change it if it breaks, that is: STL first, 3rd party headers second, your own middleware stack third, the current project includes fourth and the current library includes fifth. This way, if there is a conflict, hopefully the error will point to a header of yours.

  • Put the public stuff before the private stuff in a class. Clients of the class are only concerned with the public interface, no need to have them wade through all the dirty implementation details before they can get to it.

Upvotes: 4

Tamer Shlash
Tamer Shlash

Reputation: 9523

It's because you've #include "MeshList.h" in the file Mesh.h, so the file MeshList.h will be compiled first, and the class Mesh is not yet declared. For that the compiler will think that Mesh in the error line is a variable name that has not got a type before it, hence the error.

This is an example of making a friend member function:

#include <iostream>


class foo;

class bar
{
public:
    void barfunc(foo &f);
};

class foo
{
private:
    friend void bar::barfunc(foo &f);
    int i;
public:
    foo()
    {
        i = 0;
    }
    void printi()
    {
        std::cout << i << '\n';
    }
};

void bar::barfunc(foo &f)
{
    f.i = 5;
}


int main()
{
    foo f;
    bar b;
    b.barfunc(f);
    f.printi();
    return 0;
}

Upvotes: 6

Gareth McCaughan
Gareth McCaughan

Reputation: 19981

When you compile Mesh.cpp, it includes Mesh.h, which includes MeshList.h, which starts to include Mesh.h but stops early because _MESH_H is now defined. Then (back in MeshList.h) there's a reference to Mesh -- but that hasn't been declared yet. Hence, e.g., your C2143 error.

Upvotes: 9

Related Questions