最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何在C++類的外部調(diào)用類的私有方法

 更新時間:2022年09月09日 10:45:15   作者:ithiker  
這篇文章主要介紹了如何在C++類的外部調(diào)用類的私有方法,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下

前言

可否在C++類的外部調(diào)用類的私有方法呢?既然談到了這個問題,當(dāng)然是可以的。

問題

目標(biāo)是在一個外部function中調(diào)用Widget::forbidden()這個private function。限制條件是不能修改Widget類。

class Widget {
   private:
    void forbidden();
  };
void hijack(Widget& w) {
    w.forbidden();  // ERROR!
  }

下面我們一步步來實現(xiàn)這個小目標(biāo):

技術(shù)準(zhǔn)備

顯然,我們不可能直接像w.forbidden()類似那樣調(diào)用,我們必須通過PMF(pointer to member function)來間接調(diào)用,所以,下面先簡要介紹PMF:

1. pointers to member functions

由于下面會廣泛的用到pointers to member functions (PMFs),我們先來回顧一下它的用法:

class Calculator {
  float current_val = 0.f;
 public:
   void clear_value() { current_val = 0.f; };
   float value() const {
     return current_val;
   };

   void add(float x) { current_val += x; };
   void multiply(float x) { current_val *= x; };
};

在C++11中,使用函數(shù)指針調(diào)用函數(shù),我們可以這么做:

  using Operation = void (Calculator::*)(float);
 
  Operation op1 = &Calculator::add;
  Operation op2 = &Calculator::multiply;
  
  using Getter = float (Calculator::*)() const;
  Getter get = &Calculator::value;
  
  Calculator calc{};
  (calc.*op1)(123.0f); // Calls add
  (calc.*op2)(10.0f);  // Calls multiply
  // Prints 1230.0
  std::cout << (calc.*get)() << '\n';

函數(shù)指針的一個特性是它可以綁定到類的是由成員函數(shù),下面我們將會用到這點,假設(shè)Widget類提供了某種機(jī)制來獲取其私有成員函數(shù)的方法,那么,實現(xiàn)我們的目標(biāo)就可以像下面這樣做:

class Widget {
 public:
  static auto forbidden_fun() {
    return &Widget::forbidden;
  }
 private:
  void forbidden();
};

void hijack(Widget& w) {
  using ForbiddenFun = void (Widget::*)();
  ForbiddenFun const forbidden_fun =
    Widget::forbidden_fun();

  // Calls a private member function on the Widget
  // instance passed in to the function.
  (w.*forbidden_fun)();
}

采用這種辦法不錯,但是別忘了,我們不能修改Widget類,大多數(shù)的類也不提供返回私有成員的方法。

既然我們不能通過添加函數(shù)返回PMF的方法來進(jìn)行實踐,那么,是否還有其它方法可以在類的外部訪問類的私有成員呢?C++提供了顯式模板特化的方法。

2. The explicit template instantiation

C++ 標(biāo)準(zhǔn)中提到:

17.7.2 (item 12)
The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible.]

顯式模板特化時,名字訪問規(guī)則此時不起作用,Stack Overflow 給出了一個例子:

class Foo
{
private:
  struct Bar;

  template<typename T> class Baz { };

public:
  void f();  // does things with Baz<Bar>
};

// explicit instantiation declaration
extern template class Foo::Baz<Foo::Bar>;

這里在顯式模板特化Foo::Baz時,可以訪問Foo::Baz和Foo::bar

到這里,既然成員訪問規(guī)則不適用于模板特化,模板特化時可以訪問類的私有成員,那么,我們可否將PMF作為模板參數(shù)呢,下面將介紹這點。

3. Passing a member-function pointer as a non-type template parameter

在C++中模板參數(shù)通常分為兩類:

  • 類型參數(shù),一般的參數(shù)都是類型參數(shù),用來進(jìn)行類型替換;
  • 非類型參數(shù),常見的是整型或指針類型的非類型參數(shù)

比如下面,其中T是類型參數(shù),size是非類型參數(shù):

template <class T, int size> // size is the non-type parameter
class StaticArray
{
private:
    // The non-type parameter controls the size of the array
    T m_array[size];
 
public:
    T* getArray();
	
    T& operator[](int index)
    {
        return m_array[index];
    }
};

下面我們再看一個指針作為非類型參數(shù)的例子:

class SpaceShip {
 public:
  void dock();
  // ...
};

// Member function alias that matches the
// signature of SpaceShip::dock()
using SpaceShipFun = void (SpaceShip::*)();

// spaceship_fun is a pointer-to-member-function
// value which is baked-in to the type of the
// SpaceStation template at compile time.
template <SpaceShipFun spaceship_fun>
class SpaceStation {
  // ...
};

// Instantiate a SpaceStation and pass in a
// pointer to member function statically as a
// template argument.
SpaceStation<&SpaceShip::dock> space_station{};

上面的別名SpaceShipFun的使用使得SpaceStation只能用SpaceShip::dock的PMF來實例化,這個模板顯得不那么通用,所以我們可以將函數(shù)指針也作為一個模板參數(shù):

template <
  typename SpaceShipFun,
  SpaceShipFun spaceship_fun
>
class SpaceStation {
  // ...
};

// Now we must also pass the type of the pointer to
// member function when we instantiate the
// SpaceStation template.
SpaceStation<
  void (SpaceShip::*)(),
  &SpaceShip::dock
> space_station{};

當(dāng)然,我們也可以更進(jìn)一步,在實例化模板的時候讓編譯器自動推導(dǎo)函數(shù)指針的類型:

 SpaceStation<
    decltype(&SpaceShip::dock),
    &SpaceShip::dock
  > space_station{};

到這里,我們似乎找到了解決方案,通過顯示模板特化導(dǎo)出本來對外部不可見的函數(shù)指針,通過函數(shù)指針調(diào)用是由函數(shù)

4. Solution. Passing a private pointer-to-member-function as a template parameter

我們結(jié)合上面的3個技巧,似乎找到了解決方案:

// The first template parameter is the type
// signature of the pointer-to-member-function.
// The second template parameter is the pointer
// itself.
template <
  typename ForbiddenFun,
  ForbiddenFun forbidden_fun
> struct HijackImpl {
  static void apply(Widget& w) {
    // Calls a private method of Widget
    (w.*forbidden_fun)();
  }
};

// Explicit instantiation is allowed to refer to
// `Widget::forbidden` in a scope where it's not
// normally permissible.
template struct HijackImpl<
  decltype(&Widget::forbidden),
  &Widget::forbidden
>;

void hijack(Widget& w) {
    HijackImpl<
      decltype(&Widget::forbidden),
      &Widget::forbidden
    >::apply(w);
  }

運行一下,發(fā)現(xiàn)其實不可行:

error: 'forbidden' is a private member of 'Widget'
   HijackImpl<decltype(&Widget::forbidden),
     &Widget::forbidden>::hijack(w);

主要的原因是因為在hijack函數(shù)中使用HijackImpl<decltype(&Widget::forbidden), &Widget::forbidden>::apply(w)不是顯式模板特化,它只是常見的隱式模板實例化。

那么,如何采用顯示模板特化的方法你?這就需要使用friend技巧:

5. Friend

我們通常這樣定義和使用友員函數(shù):

class Gadget {
  // Friend declaration gives `frobnicate` access
  // to Gadget's private members.
  friend void frobnicate();

 private:
  void internal() {
    // ...
  }
};

// Definition as a normal free function
void frobnicate() {
  Gadget g;
  // OK because `frobnicate()` is a friend of
  // `Gadget`.
  g.internal();
}

但是不會這樣:

class Gadget {
  // Free function declared as a friend of Gadget
  friend void frobnicate() {
    Gadget g;
    g.internal(); // Still OK
  }

 private:
   void internal();
};

void do_something() {
  // NOT OK: Compiler can't find frobnicate()
  // during name lookup
  frobnicate();
}

因為frobnicate在Gadget內(nèi)部,do_something()在做名字查找時不會查找到frobnicate,解決辦法如下:

class Gadget {
  friend void frobnicate(Gadget& gadget) {
    gadget.internal();
  }

 private:
   void internal();
};

void do_something(Gadget& gadget) {
  // OK: Compiler is now able to find the
  // definition of `frobnicate` inside Gadget
  // because ADL adds it to the candidate set for
  // name lookup.
  frobnicate(gadget);
}

當(dāng)然如果do_something不帶參數(shù),我們在外部重新聲明友員函數(shù),也是可以的:

class Gadget {
  // Definition stays inside the Gadget class
  friend void frobnicate() {
    Gadget g;
    g.internal();
  }

 private:
   void internal();
};

// An additional namespace-scope declaration makes
// the function available for normal name lookup.
void frobnicate();

void do_something() {
  // The compiler can now find the function
  frobnicate();
}

了解了這些,對下面這樣的代碼就不會太吃驚了:

#include <iostream>

template <int N>
class SpookyAction {
  friend int observe() {
    return N;
  }
};

int observe();

int main() {
  SpookyAction<42>{};
  std::cout << observe() << '\n';  // Prints 42
}

Put the magic pieces together

那么結(jié)合上面所有的這些技巧,我們就可以得到下面的代碼:

namespace {
// This is a *different* type in every translation
// unit because of the anonymous namespace.
struct TranslationUnitTag {};
}
void hijack(Widget& w);
template <
  typename Tag,
  typename ForbiddenFun,
  ForbiddenFun forbidden_fun
> class HijackImpl {
  friend void hijack(Widget& w) {
    (w.*forbidden_fun)();
  }
};

// Every translation unit gets its own unique
// explicit instantiation because of the
// guaranteed-unique tag parameter.
template class HijackImpl<
  TranslationUnitTag,
  decltype(&Widget::forbidden),
  &Widget::forbidden
>;

完整代碼可以參見這里:@wandbox

總結(jié)一下:

我們通過顯式模板特化,將Widget::forbidden的函數(shù)指針傳遞給了HijackImpl示例,特化了模板函數(shù)hijack通過友員函數(shù)聲明,讓hijack函數(shù)可以在他處被調(diào)用,從而達(dá)成了小目標(biāo) 結(jié)語

寫這些,是希望你將上面的代碼用于實際產(chǎn)品代碼嗎?絕對不要這么做! 不要違背C++的封裝的原則,一旦打開潘多拉魔盒,那么你的產(chǎn)品代碼雖然看起來可運行,但是將不可維護(hù),不可知,因為你破壞了我們和C++語言以及程序員之間的合約。

到此這篇關(guān)于如何在C++類的外部調(diào)用類的私有方法的文章就介紹到這了,更多相關(guān)C++類外部調(diào)用類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言題目:有多少張桌子--并查集

    C語言題目:有多少張桌子--并查集

    并查集是一種用于管理分組的數(shù)據(jù)結(jié)構(gòu)。它具備兩個操作:(1)查詢元素a和元素b是否為同一組 (2) 將元素a和b合并為同一組,需要的朋友可以參考下
    2021-09-09
  • opencv求解區(qū)域的內(nèi)接矩形

    opencv求解區(qū)域的內(nèi)接矩形

    這篇文章主要為大家詳細(xì)介紹了opencv求解區(qū)域的內(nèi)接矩形,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C語言實現(xiàn)文件內(nèi)容按行隨機(jī)排列的算法示例

    C語言實現(xiàn)文件內(nèi)容按行隨機(jī)排列的算法示例

    這篇文章主要介紹了C語言實現(xiàn)文件內(nèi)容按行隨機(jī)排列的算法,涉及C語言字符串、數(shù)組遍歷與隨機(jī)數(shù)相關(guān)算法實現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • C++面試八股文之了解auto關(guān)鍵字

    C++面試八股文之了解auto關(guān)鍵字

    這篇文章主要為大家介紹了C++面試八股文之了解auto關(guān)鍵字問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • C語言popen函數(shù)調(diào)用其他進(jìn)程返回值示例詳解

    C語言popen函數(shù)調(diào)用其他進(jìn)程返回值示例詳解

    這篇文章主要為大家介紹了C語言popen函數(shù)調(diào)用其他進(jìn)程返回值示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • C++日期類運算符重載方式

    C++日期類運算符重載方式

    這篇文章主要介紹了C++日期類運算符重載方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • C++模擬2D牛頓力學(xué)效果的示例代碼

    C++模擬2D牛頓力學(xué)效果的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用C++模擬2D牛頓力學(xué)效果,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下
    2023-06-06
  • pthread_cond_wait() 用法深入分析

    pthread_cond_wait() 用法深入分析

    以下是對pthread_cond_wait的用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-07-07
  • C語言經(jīng)典順序表真題演練講解

    C語言經(jīng)典順序表真題演練講解

    程序中經(jīng)常需要將一組數(shù)據(jù)元素作為整體管理和使用,需要創(chuàng)建這種元素組,用變量記錄它們,傳進(jìn)傳出函數(shù)等。一組數(shù)據(jù)中包含的元素個數(shù)可能發(fā)生變化,順序表則是將元素順序地存放在一塊連續(xù)的存儲區(qū)里,元素間的順序關(guān)系由它們的存儲順序自然表示
    2022-04-04
  • C++入門淺談之類和對象

    C++入門淺談之類和對象

    C++ 是一門面向?qū)ο蟮木幊陶Z言,理解 C++,首先要理解類(Class)和對象(Object)這兩個概念。下面和小編一起來學(xué)習(xí)吧
    2021-10-10

最新評論

榆社县| 托克托县| 水城县| 肥西县| 浑源县| 车险| 亳州市| 阳西县| 常州市| 若尔盖县| 汤原县| 延吉市| 莲花县| 龙川县| 峨眉山市| 民和| 大姚县| 蒲城县| 三都| 苏尼特左旗| 抚远县| 吉林省| 五原县| 巴中市| 宜都市| 郯城县| 乌鲁木齐县| 柯坪县| 梁河县| 梨树县| 福清市| 巴楚县| 云南省| 隆林| 涪陵区| 长葛市| 界首市| 贺兰县| 南召县| 通海县| 天镇县|