将 .h(库)文件中的函数声明为 class 的友元

declare a function in a .h (library) file as a friend to a class

我正在为产品列表编写一个简单的 class,我需要重载提取 >> 运算符和插入 << 运算符以写入文件和从文件读取

student_projectV1/list.h

#ifndef STUDENT_PROJECTV1_LIST
#define STUDENT_PROJECTV1_LIST


#include <fstream>



namespace list {

    class list {

        private:

            string name;
            int price;
            short quantity;


        public:

            ofstream ofs;
            ifstream ifs;


            // file_mutators
            void set_created_file () noexcept(false) ;
            void set_readable_file ();

            // constructors
            list() noexcept ( noexcept ( set_created_file() ) )  ;

            list ( string  , int , short  ) noexcept(false) ;
            list ( class list &) noexcept ( noexcept ( set_created_file() ) ) ;

            // initialization to cover after construction of an obj
            void initialize ( string , int , short ) noexcept(false) ;

            // mutators
            void set_name ( string ) noexcept(false);
            void set_price ( int ) noexcept(false) ;
            void set_quantity ( short ) noexcept(false) ;


            // accessors

            string get_name ( ) const  noexcept;
            int get_price () const noexcept;
            int get_quantity () const noexcept;


            // Enqueries
            bool check_created_file () noexcept;
            bool check_opened_file();

            // destructor
            ~list();



            // friend global functions
            // overloaded operators

            friend ofstream & friend_global_funcs :: operator << ( ofstream & , class list::list &) ;
            friend ifstream &  friend_global_funcs :: operator >> ( ifstream & , class list::list &) ;


    };
}


#endif

现在我打算将这两个重载函数的定义放在命名空间

内的另一个 friend_global_funcs.h文件中
friend ofstream & friend_global_funcs :: operator << ( ofstream & , class list::list &) ;
friend ifstream &  friend_global_funcs :: operator >> ( ifstream & , class list::list &) ;

friend_global_funcs.h

//
// Created by solo-l-ub on 2/27/22.
//

#ifndef STUDENT_PROJECTV1_FRIEND_GLOBAL_FUNCS_H
#define STUDENT_PROJECTV1_FRIEND_GLOBAL_FUNCS_H





namespace friend_global_funcs {

    ofstream & operator<< (ofstream &ofs, class list::list &l)  {

        if (!l.check_created_file())
            throw new file_missing::file_missing(
                    "can not write info to file something wrong with acquiring file in constructor of obj \n");


        ofs << l.name() << "\t" << l.price << "\t" << l.quantity << "\n";

        return ofs;

    }


    ifstream & operator>>(ifstream &ifs, list :: list &l) {

        l.set_readable_file();

        if (!l.check_opened_file())
            throw new file_missing::file_missing(
                    "can't retrieve data cuz file is not associated with obj currently I'm in operated >> overloaded fuc \n");


        ifs >> l.name >> l.price >> l.quantity;

        return ifs;
    }

}

#endif //STUDENT_PROJECTV1_FRIEND_GLOBAL_FUNCS_H

现在我在 main.cpp 文件中包含文件顺序的方案我首先包含 class list.h 然后是 friend_global_funcs.h file

main.cpp



#include <iostream>
using namespace std;

// using namespaces


// classes
#include "file_missing.h"
#include "empty.h"
#include "list.h"


// libraries
#include "friend_global_funcs.h"

int main() {



    //////////////////////////////////////

    return 0;
}


现在,当我尝试在终端中使用 g++ 进行编译时

g++ main.cpp -o out

我得到的错误是 list.h 中的重载函数没有声明,即使我已经使用范围解析运算符告诉编译器确切在哪里寻找函数 friend_global_funcs :: operator <<

g++ 终端错误

In file included from main.cpp:33:
list.h:64:31: error: ‘friend_global_funcs’ has not been declared
   64 |             friend ofstream & friend_global_funcs :: operator << ( ofstream & , class list::list &) ;

现在我已经上传了我的项目,这是一个非常非常轻的项目,只是为了练习从 classes 到 github 中写入和读取文件 如果您想查看它并指导我正确的顺序来使用在 class

中的另一个 .h 文件中定义的函数的友谊

github 源 https://github.com/moji2013/student_projectV1.git

list.h 包括 friend_global_funcs.h,friend_global_funcs.h 包括 list.h。这行不通。 A 不能在 B 之前,而 B 也在 A 之前。 header 守卫防止无限递归,但你最终会得到一个 header 是第一个,因此没有它所依赖的声明。

正确的解决方案是遵循以下顺序:

  • 声明list::list
  • 声明 friend_global_funcs::operator<<friend_global_funcs::operator>>
  • 定义list::list
  • 定义 friend_global_funcs::operator<<friend_global_funcs::operator>>

P.S。如果您在 header 文件中定义一个函数,那么您应该使该函数内联,这样您就不会因将 non-inline 定义包含到多个翻译单元而意外违反一个定义规则。您还应该考虑为什么要在 header 文件中定义函数。

我知道我可以将友元函数的定义放在 list.cpp 文件中并省去所有麻烦,但我想练习并发现是否可以向另一个函数声明友元函数 (library.h) 文件

问题 是在您当前的 list.h 中,您有 2 个 朋友声明 使用 scope resolution operator :: 引用命名空间 friend_global_funcs 内的 operator<<operator>>。在这些友元声明点,编译器不知道命名空间 friend_global_funcs.

所以要解决这个你需要告诉编译器有一个名为friend_global_funcs的命名空间,其中包含operator<<和[=16的声明=] 如下图(Working Demo):

list.h

#ifndef STUDENT_PROJECTV1_LIST
#define STUDENT_PROJECTV1_LIST


#include <fstream>
#include <string>
namespace list {
    class list;
}
namespace friend_global_funcs {

    std::ofstream & operator << ( std::ofstream & , const list::list &) ; //const added here and keyword class removed from second parameter
    std::ifstream & operator >> ( std::ifstream & , list::list &) ;//keyword class removed from second parameter
}

namespace list {

    class list {

        private:

            std::string name;
            int price;
            short quantity;


        public:

            std::ofstream ofs;
            std::ifstream ifs;


            // file_mutators
            void set_created_file () noexcept(false) ;
            void set_readable_file ();

            // constructors
            list() noexcept ( noexcept ( set_created_file() ) )  ;

            list ( std::string  , int , short  ) noexcept(false) ;
            list ( class list &) noexcept ( noexcept ( set_created_file() ) ) ;

            // initialization to cover after construction of an obj
            void initialize ( std::string , int , short ) noexcept(false) ;

            // mutators
            void set_name ( std::string ) noexcept(false);
            void set_price ( int ) noexcept(false) ;
            void set_quantity ( short ) noexcept(false) ;


            // accessors

            std::string get_name ( ) const  noexcept;
            int get_price () const noexcept;
            int get_quantity () const noexcept;


            // Enqueries
            bool check_created_file () const noexcept;//const added here
            bool check_opened_file();

            // destructor
            ~list();



            // friend global functions
            // overloaded operators

            friend std::ofstream & friend_global_funcs :: operator << ( std::ofstream & , const list &) ;//const added here
            friend std::ifstream &  friend_global_funcs :: operator >> ( std::ifstream & , list &) ;


    };
    
}

#endif

list.cpp

#include "list.h"
#include"empty.h"
#include "file_missing.h"
namespace list{
// constructors
list :: list () noexcept ( noexcept ( set_created_file() ) )  {
    // give 'em a valid obj
    // ,and a most common one
    name = "not_set" , price = 1 , quantity = 1;

    // not sure weather to call the
    set_created_file();
    // or simply call this
//    ofs.open("list.txt", ios::app);
}

list :: list ( std::string name , int price , short quantity ) noexcept(false) {

    set_name ( name );
    set_price ( price );
    set_quantity ( quantity );

    set_created_file();
}

list :: list ( class list &r ) noexcept ( noexcept ( set_created_file() ) )  {

    name = r.name;
    price = r.price;
    quantity = r.quantity;

    // how to copy file location then?

//    ofs = r.ofs;
    set_created_file();
}
////


// initialization to cover after construction of an obj
void list :: initialize ( std::string name , int price , short quantity ) {
    set_name ( name );
    set_price ( price );
    set_quantity ( quantity );
    set_created_file();
}
////

// mutators
void list :: set_name ( std::string name ) noexcept(false) {

    if ( name.empty() )
        throw new empty::empty ( "name can not be left out enter something \n");

    (*this).name = name;
}

void list :: set_price ( int price )  noexcept(false) {

    if ( !price )
        throw new empty :: empty ( "price can not be zero \n" );

    (*this).price = price;
}

void list :: set_quantity ( short quantity ) noexcept(false) {

    if ( !quantity )
        throw new empty :: empty ( "quantity can not be zero \n" );

    (*this).quantity = quantity;
}

/////


// file mutators
void list :: set_created_file () noexcept(false) {

    if ( !ofs.is_open() )
        ofs.open("student_list_file.txt", std::ios::app);

    if ( !ofs.is_open() )
        throw new file_missing :: file_missing ( "file couldn't be created or opened \n" );

}

void list :: set_readable_file () {

    if ( !ifs.is_open() )
        ifs.open ( "student_list_file.txt" );

    if ( !ifs.is_open() )
        throw new file_missing :: file_missing ( "file couldn't be opened by set_readable_file function \n" );

}



////


// accessors
std::string list :: get_name () const noexcept {
    return name;
}
int list :: get_price () const  noexcept {
    return price;

}
int list :: get_quantity () const  noexcept {
    return quantity;
}

///

// enqueries
bool list :: check_created_file () const noexcept{

    return ofs.is_open();
}


bool list :: check_opened_file (){

    return ifs.is_open();
}


// destructive
list :: ~list() {
    // release resources
    // close file
    // close connection
    // release heap memory

    ofs.close();
    ifs.close();

}
}

friend_global_funcs.cpp


#include "list.h"
#include "file_missing.h"
#include "empty.h"
namespace friend_global_funcs {

    std::ofstream & operator<< (std::ofstream &ofs, const list::list &l)  { //const added here

        if (!l.check_created_file())
            throw new file_missing::file_missing(
                    "can not write info to file something wrong with acquiring file in constructor of obj \n");


        ofs << l.name << "\t" << l.price << "\t" << l.quantity << "\n"; //changed l.name() to l.name

        return ofs;

    }


    std::ifstream & operator>>(std::ifstream &ifs, list :: list &l) {

        l.set_readable_file();

        if (!l.check_opened_file())
            throw new file_missing::file_missing(
                    "can't retrieve data cuz file is not associated with obj currently I'm in operated >> overloaded fuc \n");


        ifs >> l.name >> l.price >> l.quantity;

        return ifs;
    }

}

Working Demo

我所做的一些更改包括:

  1. 在文件 list.h 中添加了命名空间 friend_global_funcs。此命名空间包含 operator<<operator>>.
  2. 的前向声明
  3. 在文件 list.hfriend_global_funcs.cpp.
  4. 中的重载 operator<< 的第二个参数中添加了一个 low-level const
  5. 已将文件 friend_global_funcs.h 的名称更改为 friend_global.funcs.cpp,因为它包含实现。

PS:我花了大约 30 分钟从给定的 github 存储库创建一个工作示例。你的程序中可能还有其他我没有检查过的逻辑错误,因为回购协议很大。我专注于手头的问题(这是超载 operator<<operator>>)。