C ++:メンバーポインタのような62の---特別なツールとテクニック(追加:呼び出し可能、mem_fnのテンプレート)

  • メンバーポインタの概要:
    • これは、メンバポインタ手段のポインタは、クラスの非静的メンバを指すことができ
    • 通常の状況下では、オブジェクトへのポインタが、ポインタのメンバーは、クラスのメンバーである、オブジェクトのないクラスが出て作成します
    • 静的クラスメンバーは、通常のポインタとの間の差がないとの静的メンバへのポインタに任意のオブジェクトに属していないので、特別な点の静的メンバポインタ
    • タイプのメンバーはポインタ型とクラスメンバーの種類が含まれています。
      • これへのポインタを初期化するとき、我々はそれクラスのメンバーをポイントしますが、オブジェクトが指定されていない、メンバーが属します
      • メンバーポインタを使用するまでは、唯一の所属オブジェクトのメンバを提供
  • ここでは、に基づいて紙など、クラスを定義します。
class Screen {
public:
    typedef std::string::size_type pos;

    char get_cursor()const { return contents[cursor]; }
    char get()const;
    char get(pos ht, pos wd)const;
private:
    std::string contents;
    pos cursor;
    pos height, width;
};

まず、データメンバポインタ

データメンバポインタを定義します

  • 特長:
    • 私たちは、必要な現在の変数を示すために*ポインタであります
    • メンバーポインタを定義する場合、クラスが所属含まれている必要があります
  • ポインタを定義します
    • スクリーン・オブジェクトへのポインタの以下の定義、文字列型、ポインタ及びポインタのポインタが一定である(したがって、ポインタ値によって修正することができません)
    • ポインタの文字列の種類ので、そうポインタ定数(CONST)スクリーンオブジェクトの文字列メンバーを指すことができます
const string Screen::*pdata;
  • ポインタ代入:
//将pdata指向于Screen类的contents成员
pdata = &Screen::contents;
  • ポインタデータメンバを定義するときには、直接初期化することができる、自動またはdecltypeは、定義されました:
auto pdata = &Screen::contents;

データメンバポインタを使用します

  • ポインタが定義された後、ポインタは任意のデータメンバーを指していない、オブジェクトを指すようにポインタのみがメンバーに属していない唯一のメンバーのポインタ参照は、我々は、オブジェクトに関する情報を提供する場合
  • 例えば:
Screen myScreen;
Screen *pScreen = &myScreen;

//.*解引用*pdata以获得myScreen对象的contents成员
auto s = myScreen.*pdata; //相当于myScreen.contents

//->*解引用*pdata以获得myScreen对象的contents成员
s = pScreen->*pdata;      //相当于pScreen->contents

戻り値ポインタデータメンバ関数

  • メンバーポインタが上で定義した場合には、民間の内容のでpDataをではなく、Screenクラスの外に表示されます(ただ、上記の記述を実証します)
  • カプセル化を反映するために、典型的には、我々はメンバ関数を定義し、関数は、メンバーへのポインタを返します
  • 例えば:
class Screen {
public:
    //成员函数,返回一个成员的指针
    static const std::string Screen::*data() { 
        return &Screen::contents; 
    }
private:
    std::string contents;
};
  • 関数がstaticとして定義されている私たちは、会員ポインタのためにこの関数を直接呼び出すことができるように、我々は、それは、任意のオブジェクトに属していないだろう
  • 我々は、データを呼び出すときは、メンバへのポインタを取得することができます。例えば:
int main()
{
    //调用data()静态函数来获得一个成员的指针
    const std::string Screen::*pdata = Screen::data();
    return 0;
}
  • 定義されたら、我々は通常のポインタを使用することができます。例えば:
int main()
{
    Screen *pScreen = new Screen;

    const std::string Screen::*pdata = Screen::data();
    auto s = pScreen->*pdata; //等价于pScreen->contents

    return 0;
}

第二に、メンバ関数ポインタ

  • そして、同様のデータメンバを指すポインタが、我々はメンバ関数へのポインタへのポインタを宣言することができます
  • 構文注:
    • メンバ関数へのポインタも必要と関数の戻り値の型とパラメータリストをターゲットに
    • constメンバ関数、またはメンバーを参照する場合は、我々はしなければならないのconst修飾子修飾子が含まれるか来て参照することが

メンバ関数ポインタの定義

  • 最も簡単な方法は、することです宣言するためにautoを使用するメンバ関数へのポインタへのポインタを
//pmf是一个函数指针,指向于get_cursor函数
auto pmf = &Screen::get_cursor;
  • メンバ関数ポインタの定義ため、ターゲット関数の戻り値の型およびパラメータリストを指定する必要がそのためではない紛争原因メンバ関数をオーバーロードしていること。例えば:
class Screen {
public:
    typedef std::string::size_type pos;

    char get_cursor()const { return contents[cursor]; }
    char get()const;
    char get(pos ht, pos wd)const;
    //...
};

int main()
{
    //pmf2是一个成员函数指针,其指向于返回值为char,形参为两个Screen::pos类型的成员函数
    char (Screen::*pmf2)(Screen::pos, Screen::pos)const;
    //为pmf2指针赋值
    pmf2 = &Screen::get;
    return 0;
}
  • 優先順位の検討のため、上記の定義のポインタの両側のブラケットは欠落していません括弧のこのペアがなければ、コンパイラは、ステートメントが(有効)関数の宣言だと思います。
    • エラー:コンパイラは、pは、通常の関数であり、Screenクラスのchar型のメンバを返すと思います。それが正常な機能ですので、あなたが修飾constを使用することはできません
//错误的语法,非成员函数p不能使用const限定符
char Screen::*pmf2(Screen::pos, Screen::pos)const;
  • 自動変換規則がメンバと関数メンバへのポインタとの間に存在しないこと、通常の関数ポインタ。
char (Screen::*pmf2)(Screen::pos, Screen::pos)const;

pmf2 = &Screen::get; //正确
pmf2 = Screen::get;  //错误,缺少&。在成员函数和指针之间不存在自动转换规则

メンバ関数ポインタ

  • そして、ポインタのデータメンバを使用して、我々はする必要があります> *演算子クラスのメンバ関数を呼び出すために、メンバ関数へのポインタをスコープ- *または使用します
  • 例えば:
//pmf为成员函数指针,指向于get_cursor()函数
auto pmf = &Screen::get_cursor;
//pmf2为成员函数指针,指向于带有两个参数的get()函数
char (Screen::*pmf2)(Screen::pos, Screen::pos)const = &Screen::get;

Screen myScreen;
Screen *pScreen = &myScreen;

char c1 = (myScreen.*pmf)();     //等价于myScreen.get_cursor()
char c2 = (pScreen->*pmf2)(0, 0);//等价于pScreen->get(0,0)
  • 上記では、メンバ関数へのポインタを参照解除するとき両側にブラケットを追加する必要がない場合、それは間違っているだろう。
    • 間違った理由:私たちはPMFとpmf2という名前の関数を呼び出し、その後、メンバー演算子へのポインタとして、これらの関数の戻り値を使用*としたい - > *計算は思います。しかしpmf2とPMF機能ではありませんので、コードのエラー
char c1 = myScreen.*pmf();      //错误的
//其等价于myScreen.*(pmf())

char c2 = pScreen->*pmf2(0, 0); //错误的
//其等价于myScreen->*(pmf2(0,0))

タイプエイリアスのメンバーポインタの使用

  • エイリアスを使用するかのtypedefを入力し、ポインタのメンバーが容易に理解することができます
  • たとえば、アクションパラメータの別名の以下のタイプがGET二つの機能の同義語のように定義されます。
//Action是一种可以指向Screen成员函数的指针,其接受两个pos实参,返回值类型为char
using Action = char (Screen::*)(Screen::pos, Screen::pos)const;
  • 今、私たちは、メンバ関数ポインタを定義することがより便利であるとき:
//get是一个指向成员函数的指针
Action get = &Screen::get;
  • そして、他の同様の関数ポインタは、我々はできる関数の戻り値の型やパラメータの型として、メンバ関数へのポインタですメンバーへのポインタ前記パラメータは、デフォルト引数を持つことができます
using Action = char (Screen::*)(Screen::pos, Screen::pos)const;

//action是一个函数,其中参数2为一个指针,并且其有默认实参,指向于Screen的get成员函数
Screen& action(Screen&, Action = &Screen::get);
  • 我々は、唯一のアクション関数を呼び出すとき:着信画面の要件を満たすために、関数のポインタまたはアドレスを
using Action = char (Screen::*)(Screen::pos, Screen::pos)const;
Screen& action(Screen&, Action = &Screen::get);

int main()
{
    Screen myScreen;
    Action get = &Screen::get;

    action(myScreen);               //使用默认实参
    action(myScreen, get);          //参数2调用前面定义的指针变量get
    action(myScreen, &Screen::get); //参数2显式地传入地址
    return 0;
}

メンバー関数ポインタテーブル

  • メンバ関数への通常の関数ポインタ及びポインタのため、一般的な使用は、機能テーブルに格納することです
  • クラスは、同じタイプの複数のメンバーが含まれている場合は、そのような表は、これらのメンバーのいずれかを選択するために私たちを助けることができます
  • Screenクラスは、いくつかの想定メンバ関数を含んでおり、各機能は、指定された方向にカーソルを移動させる責任があります。
class Screen {
public:
    typedef std::string::size_type pos;
	
    //移动光标的一系列函数
    Screen& home();
    Screen& forward();
    Screen& back();
    Screen& up();
    Screen& down();
private:
    pos cursor; //光标
};
  • これらの機能は共通している:任意のパラメータを受け取り、戻り値は、画面のカーソルの動きの発生に参照されません
  • 今、私たちは、デザイン関数テーブルを開始します:
    • このように、第一の静的メンバメニューを定義する前に、部材は、カーソル移動関数へのポインタのアレイであります
    • 移動機能を使用すると、上記の任意の関数を呼び出すと、対応する操作を行うことができ、定義されています
    • 質量参加のために設計された列挙関数
class Screen {
public:
    typedef std::string::size_type pos;
	
    Screen& home();
    Screen& forward();
    Screen& back();
    Screen& up();
    Screen& down();

    //函数指针
    using Action = Screen& (Screen::*)();

    //定义一个枚举
    enum Directions { HOME, FORWARD, BACK, UP, DOWN };
    //参数使用枚举来调用函数表中对应的函数
    Screen& move(Directions cm)
    {
        //必须使用this
        return (this->*Menu[cm])();
    }
private:
    pos cursor;

    static Action Menu[]; //函数表
};

//初始化函数表,将内部移动光标的函数都添加进去
Screen::Action Screen::Menu[] = {
    &Screen::home,&Screen::forward,
    &Screen::back,&Screen::up,
    &Screen::down };
  • 今、私たちは移動機能を呼び出すことができます。
int main()
{
    Screen myScreen;
    myScreen.move(Screen::HOME); //调用muScreen.home
    myScreen.move(Screen::DOWN); //调用muScreen.down

    return 0;
}

呼び出し可能オブジェクトとして第三に、メンバ関数

ポインタのメンバーは呼び出し可能ではありません

  • コールへ> *演算子-私たちが知っている上に、メンバ関数ポインタを呼び出したいを通じて、クラスまたは*オペレータが満たす必要があります。したがって、通常の関数ポインタ、ポインタが呼び出し可能のメンバーではない、そのようなポインタは、関数呼び出し演算子をサポートしていません:(関数呼び出しオペレータラムダ参照https://blog.csdn.net/qq_41453285/article/details/96118675を
  • ポインタのメンバーは呼び出し可能ではないので、私たちは直接アルゴリズムに渡されたメンバ関数へのポインタにはできません
  • 例えば、以下は、最初にベクターに空の文字列を見つけます。
std::vector<std::string> svec;
auto fp = &std::string::empty; //fp指向string的empty函数,fp是一个成员函数指针

//错误,必须使用.*或->*调用成员指针fp
std::find_if(svec.begin(), svec.end(), fp);
  • アルゴリズムfind_if 呼び出し可能オブジェクトを必要とし、我々はそれに与えられるメンバ関数ポインタfpへのポインタです。したがって、find_if内部ソースコードの形態を実行するコンパイルしないようにつながります。
//检查对当前元素的断言是否为真
if(fp(*it))  //错误,想要通过成员指针调用函数,必须使用->*运算符
  • どうやらコールしようとしてステートメントが入ってくるオブジェクト、関数ではなく

①呼び出し可能オブジェクトを生成する関数を使用します

  • 関数テンプレートを参照してください:https://blog.csdn.net/qq_41453285/article/details/95184168
  • メンバ関数ポインタに呼び出し可能オブジェクトのポイントを得る一つの方法は、関数テンプレートを使用することです。次のコードは正しいです。
std::vector<std::string> svec;
	
//empty函数的返回值为bool,参数为const string&
function<bool(const std::string&)> fcn = &std::string::empty;

//现在是正确的了,fcn是一个可调用对象,使用.*调用empty
std::find_if(svec.begin(), svec.end(), fcn);
  • 関数オブジェクトは、メンバ関数を指すポインタが含まれている場合、F 慰めクラスは、関数呼び出しを実行するためにメンバーのオペレータに正しいポインタを使用しなければならないことを知っています言い換えれば、我々はfind_if内部に次のコードに似ていると言うことができます:
//假设it是find_if内部的迭代器,则*it是一个string对象
if(fcn(*it)) //fcn就是empty的函数指针,等价于empty(*it)
  • 前記は、機能部材オペレータに正しいポインタを使用します。本質的には、以下のフォームへの関数呼び出しに基づいて機能:
if(((*it).*p)) //假设p是fcn内部的一个指向成员函数的指针,此案例中为指向于empty函数的指针
  • 次のコードは、上記の原理は類似しているコール
std::vector<std::string*> svec;

//empty函数的返回值为bool,参数为const string*	
function<bool(const std::string*)> fcn = &std::string::empty;

//现在是正确的了,fcn是一个可调用对象,使用->*调用empty
std::find_if(svec.begin(), svec.end(), fcn);

②使用は呼び出し可能オブジェクトを生成mem_fnの

  • 上記のノウハウを通じて、機能を使用する場合は、フォームは、コールのメンバーに提供されなければなりません。また、標準ライブラリ関数を使用することで、別のアプローチを取ることができ、コンパイラは、タイプの責任あるメンバーを推測できるように、mem_fnのを
  • mem_fnのは、機能ヘッダファイルに定義されていてもよく部材呼び出し可能からポインタを生成します
  • そして、機能が異なります。
    • mem_fnのオブジェクトタイプを呼び出し、ポインタ型推論部材に応じてもよい、ユーザが明示的に指定せずに
    • 例えば:私たちは、mem_fnのは呼び出し可能オブジェクトを生成使用し、オブジェクトは、(コンパイラが自動的に推論)文字列の引数を取り、ブール値を返します。
std::find_if(svec.begin(), svec.end(), mem_fn(&std::string::empty));
  • 生成されたmem_fnの呼び出し可能オブジェクトのオブジェクトの呼び出しでは、ポインタで呼び出すことができます。
std::vector<std::string> svec;
	
auto f = mem_fn(&std::string::empty); //f接受一个string或者一个string*

f(*svec.begin()); //正确,传入一个string对象,f使用.*调用empty
f(&svec[0]);      //正确,传入一个string的指针,f使用.*调用empty
  • 実際には、我々は、mem_fnのは、呼び出し可能オブジェクトがオーバーロードされた関数呼び出し演算子を含んで生成すると考えることができます:文字列*、他の許容可能な文字列&を受け入れます

③バインドを使用すると、呼び出し可能なを生成し、

  • 完全に、私たちもできるから生成された呼び出し可能オブジェクトのバインドメンバ関数を使用します。
std::vector<std::string> svec;

//选择范围中的每个string,并将其bind到empty的第一个隐式实参上
auto it = find_if(svec.begin(), svec.end(), bind(&string::empty, _1));
  • 機能と同様の場所がある、我々がバインドを使用する場合、関数は、明示的、暗黙のパラメータに変換されたオブジェクトを示すために実行する必要がありますmem_fnのと同様の場所、バインド発生呼び出し可能オブジェクトは最初の引数であってもよいが、文字列へのポインタであり、文字列が挙げられます。
std::vector<std::string> svec;

auto f = bind(&string::empty._1);
f(*svec.begin()); //正确,传入一个string对象,f使用.*调用empty
f(&svec[0]);      //正确,传入一个string的指针,f使用.*调用empty

 

发布了1504 篇原创文章 · 获赞 1063 · 访问量 43万+

おすすめ

転載: blog.csdn.net/qq_41453285/article/details/104710649