0

I am a beginner learning C++. While studying the topic of friends, I would like to try establishing a friend relationship between the class A in a.h and the class B in b.h, specifically for the get_value() member function. this is my code:

// a.h
#ifndef A_H
#define A_H
#include "b.h"

class A;

class A {
    friend void B::get_value(A&);
private:
    double m;
    int n;
public:
    A(double m = 100, int n = 200) : m(m), n(n) {}
};

#endif
// b.h
#ifndef B_H
#define B_H
class A; 

class B {
public:
    void get_value(A&);
};

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

void B::get_value(A& a) {
    std::cout << a.m << a.n << std::endl; 
    return;
}
#endif
// test.cpp
#include "a.h" 
#include "b.h" 

int main() {
    A a; 
    B b; 
    b.get_value(a); 
    return 0;
}

Writing it like this would result in an error, and the error message would be as follows:

E:/project/cpp_project/cpp_study/1. complex/b.h: In member function 'void B::get_value(A&)':
E:/project/cpp_project/cpp_study/1. complex/b.h:18:18: error: invalid use of incomplete type 'class A'
   18 |     std::cout << a.m << a.n << std::endl; 

I tried alternative approache: define B::get_value(A&) in a.h:

// a.h
#ifndef A_H
#define A_H
#include "b.h"

class A;

class A {
    friend void B::get_value(A&);
private:
    double m;
    int n;
public:
    A(double m = 100, int n = 200) : m(m), n(n) {}
};

#include <iostream>
void B::get_value(A& a) {
    std::cout << a.m << a.n << std::endl; // 访问A类的私有成员m和n
    return;
}

#endif
// b.h
#ifndef B_H
#define B_H
class A; // 前置声明A类

class B {
public:
    void get_value(A&);
};

#endif

It doesn't work but the error message has changed:

E:/project/cpp_project/cpp_study/1. complex/a.h:21: multiple definition of `B::get_value(A&)'; CMakeFiles/cpp_study.dir/1._complex/test.cpp.obj:E:/project/cpp_project/cpp_study/1. complex/a.h:21: first defined here
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

Finally, I reached out to New Bing for help, and I received the answer. It move the definition of B::get_value() into b.cpp:

// a.h
#ifndef A_H
#define A_H
#include "b.h"

class A;

class A {
    friend void B::get_value(A&);
private:
    double m;
    int n;
public:
    A(double m = 100, int n = 200) : m(m), n(n) {}
};

#endif
// b.h
#ifndef B_H
#define B_H
class A; // 前置声明A类

class B {
public:
    void get_value(A&);
};

#endif
// b.cpp
#include "b.h" // 包含B类的定义
#include "a.h"
#include <iostream>

void B::get_value(A& a) {
    std::cout << a.m << a.n << std::endl; // 访问A类的私有成员m和n
    return;
}

It works but I don't understand. I hope someone can tell me why my code is error-prone and why bing's code is right.

carl ye
  • 17
  • 1
  • 3
    Your implementation of `B::get_value(A& a)` should either be inlined, or moved to a cpp file. – wohlstad May 26 '23 at 09:10
  • Why are you forward declaring the class A when you immediately provide a definition afterwards? – Eldinur the Kolibri May 26 '23 at 09:25
  • https://stackoverflow.com/questions/60860458/whats-the-real-difference-between-h-and-cpp-files – Mike Nakis May 26 '23 at 09:26
  • https://stackoverflow.com/questions/1305947/why-does-c-need-a-separate-header-file – Mike Nakis May 26 '23 at 09:27
  • https://stackoverflow.com/questions/333889/why-have-header-files-and-cpp-files – Mike Nakis May 26 '23 at 09:27
  • 1
    In other words, this question has ***absolutely nothing*** to do with `friend` declarations. This question is about the fundamental question of what goes in a header file and what goes in a cpp file. – Mike Nakis May 26 '23 at 09:29
  • @MikeNakis *"question has absolutely nothing to do with friend declarations..."* It has a lot to do with friend declarations. Header files are used for convenience. It is not necessary to use them. The question has very much to do with friend declarations. In particular, when a qualified name like `B::get_value` can be used in a friend declaration is the answer here. That is, it has nothing to do with what goes in header file and cpp file. You can name your file whatever you want, but that doesn't change the fundamental issue which is related to friend declarations. – Jason May 26 '23 at 09:33
  • 1
    @Jason: While the friend declaration does restrict your choice of the inclusion tree, the underlying problem here is that there are cycles in the inclusion graph (it is trying not to be a tree, but the language only allows trees). That problem has nothing to do with friend declarations. – Ben Voigt May 26 '23 at 21:11

0 Answers0