31
votes

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)

  • error C2143: syntax error : missing ';' before '*'
  • error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
  • error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

  • mesh.h(11) : error C2653: 'MeshList' : is not a class or namespace name

  • meshlist.cpp(5) : error C2248: 'Mesh::vboHandle_' : cannot access private member declared in class 'Mesh'
  • mesh.h(10) : see declaration of 'Mesh::vboHandle_'
  • mesh.h(8) : see declaration of 'Mesh'
  • meshlist.cpp(5) : error C2100: illegal indirection
4
You need to get out more.Mark Ransom
Try class Meshlist; instead of #include "MeshList.h"chris
@Matthieu: a brief glance says it's a really good start though.Mooing Duck
@MooingDuck: yes it is, however because the OP tries to befriend only a single method of MeshList a forward declaration does not cut it.Matthieu M.
I didn't even read this question before I upvoted @BenjaminLindley's comment, and now I'm trapped by '+1 for the title alone'.Shep

4 Answers

7
votes

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
9
votes

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.

6
votes

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;
}
4
votes

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.