【问题标题】:Using friend function correctly across multiple Classes in C++在 C++ 中的多个类中正确使用友元函数
【发布时间】:2021-08-25 04:41:14
【问题描述】:

我正在尝试使用朋友功能。该函数应该是我拥有的所有类的朋友。但我收到多个错误,其中一些是 incomplete type。以下是我拥有的文件:

ma​​in.cpp

#include <iostream>
#include "my_ClassA.h"
#include "my_ClassB.h"
#include "my_ClassC.h"
#include "my_ClassD.h"


int main()
{
    std::cout<<"Hello World";
    my_namespace::my_ClassA object1;//this will do some computation and set everything up for object1
    my_namespace::my_ClassB object2(object1);//this object2 will use object1 to further do other computation
    my_namespace::my_ClassC object3(object2);
    
    my_namespace::my_classD object4(object4);    
    //runComputation(object1, object2, object3, object4);

    return 0;
}

my_ClassA.h

#pragma once 
#include<string>
#include <vector>

//these three includes are for friend function BUT result in error incomplete type
 #include "my_ClassB.h"
 #include "my_ClassC.h"
 #include "my_ClassD.h"
/////////////////////////////////////////////
namespace my_namespace{
    class my_ClassA{
        friend void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
            
        
        private:
           std::string name;  
           std::vector<std::string> vec;
          public:
            std::vector<std::string> get_vec();
    };
    void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
    
}

my_ClassA.cpp

#include "my_ClassA.h"

std::vector<std::string> my_namespace::my_ClassA::get_vec(){
    return vec;
}

my_ClassB.h

#pragma once 
#include<string>
#include <vector>

#include "my_ClassA.h"
#include "my_ClassC.h"
#include "my_ClassD.h"
namespace my_namespace{
    class my_ClassB{
        friend void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
        private:
           std::string name;  
           std::vector<std::string> vec;
          public:
            std::vector<std::string> get_vec();
            my_ClassB(my_ClassA);
            my_ClassB(){
                ;
            }
    };
}

my_ClassB.cpp

#include "my_ClassB.h"

std::vector<std::string> my_namespace::my_ClassB::get_vec(){
    return vec;
}
my_namespace::my_ClassB::my_ClassB(my_ClassA temp_objA){
    ;
}

my_ClassC.h

#pragma once 
#include<string>
#include <vector>
#include "my_ClassB.h"
#include "my_ClassA.h"
 #include "my_ClassD.h"
namespace my_namespace{
    class my_ClassC{
        friend void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
        private:
           std::string name;  
           std::vector<std::string> vec;
           my_ClassB objB;
          public:
            my_ClassC(my_ClassB);
            std::vector<std::string> get_vec();
    };
}

my_ClassC.cpp

#include "my_ClassC.h"
#include "my_ClassB.h"
std::vector<std::string> my_namespace::my_ClassC::get_vec(){
    return vec;
}
my_namespace::my_ClassC::my_ClassC(my_ClassB temp_objB){
    ;
}

my_ClassD.h

#pragma once 
#include<string>
#include <vector>
#include "my_ClassA.h"
#include "my_ClassB.h"
 #include "my_ClassC.h"

namespace my_namespace{
    class my_ClassD{
        friend void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
        private:
           std::string name;  
           std::vector<std::string> vec;
           my_ClassA objA;
          public:
            std::vector<std::string> get_vec();
    };
}

my_ClassD.cpp

#include "my_ClassD.h"

std::vector<std::string> my_namespace::my_ClassD::get_vec(){
    return vec;
}

我尝试使用 main.cpp 中每个类的 getter。但是我的一些类有大尺寸的向量、集合等。所以我不想一次又一次地复制它们。因此,我想直接在一个名为 runComputation 的函数中访问类的数据成员。并且该函数会将创建的对象作为引用,这样就不会在 main.cpp 中发生复制。

我所拥有的是:首先我创建不同的对象,这些对象可以将先前创建的对象作为 main.cpp 中的输入。成功创建所有对象后,我想对这些对象运行一些计算。现在的问题是我可以使用在 main.cpp 文件中创建的对象的 getter 和 setter。但是这些对象内部有大向量和其他对象,因此每次我在使用 getter 的 for 循环中使用它们时都会复制它们。为了避免这种情况,我想创建一个可以将这些对象作为引用并避免复制的友元函数。我该如何解决这个问题?有没有更好的方法来实现这一点?

PS:我知道 ADL。当我写一个像friend void runComputation(someobject&amp;);这样的朋友声明时会使用ADL,然后在类之后使这个朋友函数可见void runComputation(someobject&amp;);

【问题讨论】:

  • 没有足够的时间给出正确答案,但问题可能是相互包含(例如 A.h 包括 B.h,反之亦然)。只需手动查看包含指令的结果。您可以通过在 B.h 中仅使用一个类(例如 A)来解决此问题,对于该类,时尚 class A; 的“前向声明”就足够了,没有标题。如果 B.h 中没有代码访问 A 的成员或创建对象但仅​​使用指针或引用,例如作为函数参数或成员(因为可以在不知道类的实现的情况下生成指针和引用的代码)。
  • I tried using the getters of each of the classes in the main.cpp. But some my classes have large size vectors,sets etc etc. So i do not want to copy them again and again. 只使用 getter,返回 const 引用或非 const 引用
  • @George 我在我的第二行中陈述了一个错误。检查。现在我已经强调了它。在它是普通文本之前。
  • 友谊是完全无关的——你的类之间存在循环依赖,你需要打破这种依赖。阅读您最喜欢的 C++ 书籍中的前向声明(和引用)。
  • @molbdnilo 是的,我认为这主要是因为循环依赖。我已经知道前向声明和引用。顺便说一句,我不确定我们是否可以使用返回对数据成员的引用的 getter。例如,通常当我使用普通函数时,返回对局部变量的引用是 UB。那么从 getter 返回对数据成员的引用是可以的还是一种不好的做法?

标签: c++ c++11 c++14 friend friend-function


【解决方案1】:

制作一个my_forwards.h 头文件。它包含

命名空间 my_namespace { 类 my_ClassA; 类 my_ClassB; 类 my_ClassC; 类 my_ClassD; } (或者只是在每个头文件的顶部手动编写)。

现在根本不要在其他标题中包含my_ClassA.h。如果您在 my_ClassB 中有需要定义 my_ClassA 的函数体,请将其放在 .cpp 文件中,而不是在标题中。

// my_forwards.h
namespace my_namespace {
  class my_ClassA;
  class my_ClassB;
  class my_ClassC;
  class my_ClassD;
  void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
}

// my_ClassA.h
#pragma once
#include<string>
#include <vector>
#include "my_forwards.h"
namespace my_namespace{
  class my_ClassA{
    friend void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
    
    private:
      std::string name;  
      std::vector<std::string> vec;
    public:
      std::vector<std::string> get_vec();
    };
}

// my_ClassA.cpp
#include "my_ClassA.h"
// include other my_ClassX here if needed, after my_ClassA.h

// implementation of my_ClassA:
std::vector<std::string> my_namespace::my_Class::get_vec() {
  return vec;
}

在某些情况下,这可能需要在头文件中声明析构函数/构造函数,并在 cpp 文件中执行 my_ClassA::my_ClassA()=default;

现在,您的my_ClassC 有一个my_ClassB 类型的成员变量。这是你必须在my_ClassC.h 头文件中#include "my_ClassB.h" 的情况,因为my_ClassC 需要my_ClassB 的定义。但在大多数情况下,前向声明就足够了,或者稍作修改(例如将值参数更改为引用参数)就足够了,您不必交叉包含头文件。

【讨论】:

  • 是的,我做了几乎相同的事情,只是我没有按照您的建议创建额外的文件(标题)。相反,我在需要的地方都有前向声明。以及需要的标题。该程序按预期工作。我还删除了不必要的前向声明和循环依赖。
【解决方案2】:

不要使用 return by value 避免复制大向量,返回 const 引用来访问数据或非 const 引用来修改。

例如,标题:

#pragma once 
#include<string>
#include <vector>

//these three includes are for friend function BUT result in error incomplete type
#include "my_ClassB.h"
#include "my_ClassC.h"
#include "my_ClassD.h"
/////////////////////////////////////////////
namespace my_namespace{
    class my_ClassA{
        friend void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
            
        
        private:
        std::string name;  
        std::vector<std::string> vec;
        public:
            // use this getter to modify inner data by reference 
            std::vector<std::string>& get_vec();

            // use this getter to access data by const referenct
            std::vector<std::string> const & get_vec() const;

    };
    void runComputation(my_ClassA&,my_ClassB&,my_ClassC&,my_ClassD&);
    
}

实施:

#include "my_ClassA.h"

std::vector<std::string>& my_namespace::my_ClassA::get_vec() {
    return vec;
}


std::vector<std::string> const & my_namespace::my_ClassA::get_vec() const {
    return vec;
}

【讨论】:

  • 公平点。它确实减少了一点封装(也许只公开一个 const ref 是最好的),但我认为这值得复制权衡。但是,这并不能真正解决操作的问题。
【解决方案3】:

您可能希望将类重构为函数调用链:

// class or struct which contains intermediate state
State state;
runComputation1(&state);
runComputation2(&state);
runComputation3(&state);
runComputation4(&state);

【讨论】:

  • 这是工作近似值,但在这种情况下存在陷入“上帝对象”反模式的危险,因为State 实现之一是将所有类的数据收集到一个对象中
【解决方案4】:

问题是由于不同文件的循环依赖造成的。不用包含头文件,只需对不同的类参数使用前向声明,问题就解决了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-13
    • 2018-11-06
    • 1970-01-01
    相关资源
    最近更新 更多