友元函数与命名空间

发现问题

  有以下的源代码文件:
  1. Person.h

#ifndef Person_H
#define Person_H
#include<string>
namespace Test
{
    using std::string;
    using std::ostream;
    class Person{
    private:
        string first_name_;
        string last_name_;
    public:
        Person(string fn="unknow",string ln="unknow");
        ~Person();
        //重载<<为友元函数
        friend ostream& operator<<(ostream &os,const Person &p);
    };
} // namespace Test
#endif

  2. Person.cpp

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

Test::Person::Person(string fn,string ln){
    first_name_=fn;
    last_name_=ln;
}
Test::Person::~Person(){
    //do nothing
}
std::ostream & Test::operator<<(ostream &os,const Test::Person &p){
    os<<"Name: "<<p.first_name_<<", "<<p.last_name_<<std::endl;
    return os;
}

  3. main.cpp

#include"Person.h"
#include<iostream>
int main(){
    using namespace Test;
    Person one("firstname","lastname");
    std::cout<<one;
    std::cin.get();
    return 0;
}

  对其进行编译的过程中出现了warning,其内容如下:

.\Person.cpp:11:16: warning: 'std::ostream& Test::operator<<(std::ostream&, const Test::Person&)' has not been declared within 'Test'
 std::ostream & Test::operator<<(ostream &os,const Test::Person &p){
                ^~~~
In file included from .\Person.cpp:1:
.\Person.h:16:25: note: only here as a 'friend'
         friend ostream& operator<<(ostream &os,const Person &p);
                         ^~~~~~~~

问题分析

  warning提示在Test中没有声明。因此,解决思路有两个,第一个是单独声明,另一个是将声明和定义同时进行。这里需要理解,friend友元声明只是描述了类和友元函数的关系,实际上并不是函数的声明。因此,需要在命名空间中单独声明。

解决方案

1.在命名空间中单独声明函数

  friend友元声明只是告诉Person类,这个函数是友元函数,拥有相关的访问权限。但是友元函数并不属于Person类,因此需要在命名空间中单独声明,如下所示:

//在Person.h中
namespace Test
{
    using std::string;
    using std::ostream;
    class Person{
    private:
        string first_name_;
        string last_name_;
    public:
        Person(string fn="unknow",string ln="unknow");
        ~Person();
        //重载<<为友元函数
        friend ostream& operator<<(ostream &os,const Person &p);
    };
    //函数在命名空间中声明
    ostream& operator<<(ostream &os,const Person &p);
} // namespace Test

2.在命名空间中同时声明并定义函数

  在定义函数时不使用using或者域解析运算符,直接在命名空间中进行实现,及使用namespace进行包裹。此时,由于Person的定义和operator<<函数在同一个命名空间中,因此可以看作函数声明和定义是一起实现的,不会再出现未声明的警告。如下所示:

//在Person.cpp中
namespace Test{
    Person::Person(string fn,string ln){
        first_name_=fn;
        last_name_=ln;
    }
    Person::~Person(){
        //do nothing
    }
    std::ostream & operator<<(ostream &os,const Person &p){
        os<<"Name: "<<p.first_name_<<", "<<p.last_name_<<std::endl;
        return os;
    }
}

  以上方法采用其中一种即可。


当珍惜每一片时光~