将 .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 文件中定义的函数的友谊
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;
}
}
我所做的一些更改包括:
- 在文件
list.h
中添加了命名空间 friend_global_funcs
。此命名空间包含 operator<<
和 operator>>
. 的前向声明
- 在文件
list.h
和 friend_global_funcs.cpp
. 中的重载 operator<<
的第二个参数中添加了一个 low-level const
- 已将文件
friend_global_funcs.h
的名称更改为 friend_global.funcs.cpp
,因为它包含实现。
PS:我花了大约 30 分钟从给定的 github 存储库创建一个工作示例。你的程序中可能还有其他我没有检查过的逻辑错误,因为回购协议很大。我专注于手头的问题(这是超载 operator<<
和 operator>>
)。
我正在为产品列表编写一个简单的 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 文件中定义的函数的友谊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;
}
}
我所做的一些更改包括:
- 在文件
list.h
中添加了命名空间friend_global_funcs
。此命名空间包含operator<<
和operator>>
. 的前向声明
- 在文件
list.h
和friend_global_funcs.cpp
. 中的重载 - 已将文件
friend_global_funcs.h
的名称更改为friend_global.funcs.cpp
,因为它包含实现。
operator<<
的第二个参数中添加了一个 low-level const
PS:我花了大约 30 分钟从给定的 github 存储库创建一个工作示例。你的程序中可能还有其他我没有检查过的逻辑错误,因为回购协议很大。我专注于手头的问题(这是超载 operator<<
和 operator>>
)。