一个友元类使用误区(C++)

来源:互联网 发布:手机网络电话软件排名 编辑:程序博客网 时间:2024/06/06 23:51

这个问题,我困扰了好一会,决定记录一下。

一、问题引出以及分析

问题代码简化如下:

class B;class A{    friend class B;private:    A() = default;    bool operator()(int lhs,int rhs){ return  lhs<rhs; }};class B{    //...working    add_item(int times){ pq.push(times); }private:    std::priority_queue<int,std::vector<int> ,A> pq;};
编译出错提示信息:

 'bool A::operator()(int, int)' is private

你知道问题出现在哪里吗??如果你看出来了,那么恭喜你,我是花了挺长时间纠结的。


我的理解是这样的。 类A有一个删除的默认构造函数,类的用户是没有权限新建实例的,主要是为了封装数据。我一开始以为,B是A的友元类,理论上在B的作用域中可以建立A的对象,访问A的私有接口。 这样想确实没有错误,问题出现在我是在类B的成员std::priority_queue中使用类A。 友元不具有传递性和继承性,只能在B类的作用域中可以使用,相当于在B的成员函数中使用。但是std::priority_queue中的成员不具备访问类A的权限。除非将std::priority_queue声明为类A的友元类。因此定义如下函数便报错。

 add_item(int times){ pq.push(times); }
因为在pq的成员中使用了类A的成员函数, 每次向pq插入一个元素便会调用这个类A这个成员,如下

bool operator()(int lhs,int rhs){ return  lhs<rhs; }


二、问题解决

1、很自然会想到将类A的成员权限设置为public。 这样所有类A的用户都能访问,确实能解决问题。但是这样不能对数据很好的封装。因为类A只是一个辅助类。
2、类A只是作为一个函数对象存在而已,有没有别的办法代替类A?  答案是有,使用其他函数对象(函数,函数指针,lambda表达式,重载了函数调用运算符的类),这里lambda表达式代替A, 但是这里有一个小问题。lambda是一个函数对象实例而不是一种类型(type),
而std::priority_queue接收的是需要一个类型。 这需要一个转化。

template<    class T,    class Container = std::vector<T>,    class Compare = std::less<typename Container::value_type>> class priority_queue;

可能会写出如下代码:

class B{    //...working    add_item(int times){ pq.push(times); }private:    auto Less = [](int& lhs,int rhs)->bool {return lhs<rhs;}    std::priority_queue<int,std::vector<int> ,decltype(Less)> pq;};
这也是行不通的,类在编译的时候只看声明,auto需要根据给出的lambda 表达式推断出对应的类型,因此成员Less出错,当然pq声明也就出错了。


3、接下来就开始思考如何应对这个问题。查看std::priority_queue的constructor. 自定义一个函数比较器类型。

explicit priority_queue( const Compare& compare = Compare(),                         const Container& cont = Container() );(until C++11)priority_queue( const Compare& compare, const Container& cont );(since C++11)//...省略

代码如下: 使用简单函数指针作为类型。新建一个对象需要提供这个函数指针类型的一个实例。

#include <iostream>#include<stdio.h>#include<queue>#include<vector>#include<functional>class B{    //...working    add_item(int times){ pq.push(times); }private:    std::priority_queue<int,std::vector<int> ,auto(*)(const int& ,const int&)->bool >    pq{        [](const int& lhs, const int& rhs)->bool        {            return lhs < rhs;        }    };};
在C++11。 可以使用标准库提供的函数类型(std::function)

class B{    //...working    add_item(int times){ pq.push(times); }private:    std::priority_queue<int,std::vector<int> ,std::function<bool(const int&,const int& )> >    pq{        [](const int& lhs, const int& rhs)->bool        {            return lhs < rhs;        }    };};


这个只是记录,从遇到问题到最后解决。实现了自己想到的结果。这当中难免有错误,欢迎指出来, 有好的想法欢迎提出来。





0 0
原创粉丝点击