1. 程式人生 > >一個友元類使用誤區(C++)

一個友元類使用誤區(C++)

這個問題,我困擾了好一會,決定記錄一下。

一、問題引出以及分析

問題程式碼簡化如下:

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;
        }
    };
};
這個只是記錄,從遇到問題到最後解決。實現了自己想到的結果。這當中難免有錯誤,歡迎指出來, 有好的想法歡迎提出來。



相關推薦

一個使用誤區C++

這個問題,我困擾了好一會,決定記錄一下。 一、問題引出以及分析 問題程式碼簡化如下: class B; class A { friend class B; private: A() = default; bool operator()(int lh

函式模板和模板C++

所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。 函式模板和普通函式的區別: 函式模板不允許自動型別轉化,普通函式能夠進行自動型別轉換 函式模板和普通函式在一起呼叫的規則: 1 函式模板

asp.net中公共DBHelpC#

using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using System.Configura

輸出一個字串的全排列C++

/* 實現方法: 對於一個字串"abc"輸出它的全排列,第一個字元應該分別為a,b,c;第二個字元,後面應該是除去已輸出部分的剩餘部分的全排列。 即對於"abc", 輸出 a,輸出"abc"除去'a'的全排列; 輸出 b,輸出"bc"除去'b'的全排列;

深刻理解Python中的(metaclass)

pytho light turn 理解 war highlight 參數 實例化 type 轉載地址:http://blog.jobbole.com/21351/ 另外有幾點理解記錄下: 創建一個實例時,有時會傳入參數,這些參數會同時傳入 __init__() 和 __

C++學習筆記10運算子過載,函式,

c++允許我們為運算子定義專門的函式,這被稱為運算子過載: 運算子可以簡化字串的操作,‘+’,以及使用關係運算符比較字串,[ ]運算子訪問向量中的元素; 例如: #include <iostream> #include <string> #include <

C++模板函式,宣告的三種情況

根據《C++ Primer》第三版16.4節的敘述,C++類模板友元分為以下幾種情況1.非模板友元類或友元函式。 書上給了一個例子:class Foo{    void bar();};template <class T>class QueueItem{   

C++函式和C++ friend詳解

私有成員只能在類的成員函式內部訪問,如果想在別處訪問物件的私有成員,只能通過類提供的介面(成員函式)間接地進行。這固然能夠帶來資料

一個基於ASP.NETC#的ACCESS數據庫操作

region array conn 數據庫操作類 ide try esc [] int using System; using System.Collections; using System.Collections.Specialized; using Syst

設計一個字串StringC++練習題

要求:設計一個字串類String,可以求字串長度,可以連線兩個串(如,s1=“計算機”,s2=“軟體”,s1與s2連線得到“計算機軟體”),並且過載“=”運算子進行字串賦值,編寫主程式實現:s1="電腦科學",s2=“是發展最快的科學!”,求s1和s2的串長,連線s1和s2   #incl

C++中定義一個不能被繼承的(+模板)

自從C++11標準出來之後,就有了關鍵字final可以直接宣告一個類不能被繼承。那麼,在此之前如果想要一個類不能被繼承,可能還需要下一番功夫。 文章目錄 1.宣告建構函式為私有 2.子類宣告為基類的友元類 3.虛繼承——子類

C++的“”的一個使用場景

我遇到了“必須使用友元類”的一個場景。 我遇到了A中有B,B中有A的場景。 貌似“A的一個函式,作為B的友元函式”這個需求無法被實現。 #include <stdlib.h> class MyApiCls; class TcpClientCls//用於和Serv

C++使用之前置宣告定義變數)

今天專案使用到了友元類,於是便先試著搞個簡單地使用例子 #include<iostream> class b; class a { private:int value; protected:void funprotected(); public:friend b

C++之函數和

res con 形參 display tle private 一點 second main 通過friend關鍵字,我們可以將不屬於當前類的一個函數在當前類中加以聲明,該函數便可以成為當前類的友元函數。#include<iostream>using namesp

BlinkLED 點亮第一個LED燈C#

text interval rgs pri art one namespace ane ready 界面: <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

的構建和繼承機制c++

三種 ges cnblogs private 有一個 只有一個 public 改變 帶來 構建類: 抽象:將同類事物的共同屬性和行為提取出來並將其用變量和函數表達出來; 封裝:將抽象得來的變量和函數捆綁在一起形成一個完整的類(即這類事物擁有了屬性和行為) 控制訪問權限: p

c語言數據

32位 9.png 溢出 ima con string sign unsigned 負數 (強數據類型) 1.常量   常量是程序中不可變的量   10為常量      兩種常量   #define 定義宏常量   const      #對於#define 類型的常量,c

c語言數據

nbsp tex 字符 pre 字符串 輸入 mce 一個 命令 char 類型 1.char 變量 常量   char c; 定義一個char變量   c = ‘a’ ‘a‘字符常量   char 的本質就是一個整數,只有一個字節大小的整數 2.printf 輸出ch

MSCL超級工具C#,開發人員必備,開發利器

cnblogs 分頁 導入導出 sqlserve context public 簡單 pac dir MSCL超強工具類庫 是基於C#開發的超強工具類集合,涵蓋了日常B/S或C/S開發的諸多方面,包含上百個常用封裝類(數據庫操作類全面支持Mysql、Access、Oracl

C++中的函數和

pan string 拷貝構造函數 student 私有 oid 一個 each cor 友元函數可以修改類的私有屬性,寫在類的public/private/protected底下都可以。友元函數的函數體寫在類的外面時,寫法和普通函數一樣,不需要加friend關鍵字,但函數