iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >如何在C++类的外部调用类的私有方法
  • 295
分享到

如何在C++类的外部调用类的私有方法

2024-04-02 19:04:59 295人浏览 安东尼
摘要

目录前言问题技术准备1. pointers to member functions2. The explicit template instantiation3. Passing a

前言

可否在c++类的外部调用类的私有方法呢?既然谈到了这个问题,当然是可以的。

问题

目标是在一个外部function中调用Widget::forbidden()这个private function。限制条件是不能修改Widget类。

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

下面我们一步步来实现这个小目标:

技术准备

显然,我们不可能直接像w.forbidden()类似那样调用,我们必须通过PMF(pointer to member function)来间接调用,所以,下面先简要介绍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中,使用函数指针调用函数,我们可以这么做:

  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';

函数指针的一个特性是它可以绑定到类的是由成员函数,下面我们将会用到这点,假设Widget类提供了某种机制来获取其私有成员函数的方法,那么,实现我们的目标就可以像下面这样做:

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类,大多数的类也不提供返回私有成员的方法。

既然我们不能通过添加函数返回PMF的方法来进行实践,那么,是否还有其它方法可以在类的外部访问类的私有成员呢?C++提供了显式模板特化的方法。

2. The explicit template instantiation

C++ 标准中提到:

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.]

显式模板特化时,名字访问规则此时不起作用,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

到这里,既然成员访问规则不适用于模板特化,模板特化时可以访问类的私有成员,那么,我们可否将PMF作为模板参数呢,下面将介绍这点。

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

在C++中模板参数通常分为两类:

  • 类型参数,一般的参数都是类型参数,用来进行类型替换;
  • 非类型参数,常见的是整型或指针类型的非类型参数

比如下面,其中T是类型参数,size是非类型参数:

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];
    }
};

下面我们再看一个指针作为非类型参数的例子:

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来实例化,这个模板显得不那么通用,所以我们可以将函数指针也作为一个模板参数:

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{};

当然,我们也可以更进一步,在实例化模板的时候让编译器自动推导函数指针的类型:

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

到这里,我们似乎找到了解决方案,通过显示模板特化导出本来对外部不可见的函数指针,通过函数指针调用是由函数

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

我们结合上面的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);
  }

运行一下,发现其实不可行:

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

主要的原因是因为在hijack函数中使用HijackImpl<decltype(&Widget::forbidden), &Widget::forbidden>::apply(w)不是显式模板特化,它只是常见的隐式模板实例化。

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

5. Friend

我们通常这样定义和使用友员函数:

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内部,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);
}

当然如果do_something不带参数,我们在外部重新声明友员函数,也是可以的:

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

那么结合上面所有的这些技巧,我们就可以得到下面的代码:

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

总结一下:

我们通过显式模板特化,将Widget::forbidden的函数指针传递给了HijackImpl示例,特化了模板函数hijack通过友员函数声明,让hijack函数可以在他处被调用,从而达成了小目标 结语

写这些,是希望你将上面的代码用于实际产品代码吗?绝对不要这么做! 不要违背C++的封装的原则,一旦打开潘多拉魔盒,那么你的产品代码虽然看起来可运行,但是将不可维护,不可知,因为你破坏了我们和C++语言以及程序员之间的合约。

到此这篇关于如何在C++类的外部调用类的私有方法的文章就介绍到这了,更多相关C++类外部调用类内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 如何在C++类的外部调用类的私有方法

本文链接: https://www.lsjlt.com/news/167225.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何在C++类的外部调用类的私有方法
    目录前言问题技术准备1. pointers to member functions2. The explicit template instantiation3. Passing a...
    99+
    2022-11-13
  • php无法在外部静态调用类和方法的原因是什么
    这篇文章主要讲解了“php无法在外部静态调用类和方法的原因是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php无法在外部静态调用类和方法的原因是什么”吧!什么是静态调用?在PHP开发中...
    99+
    2023-07-05
  • 如何mock当前类的私有方法
    背景 基础知识 mockito单元测试:它的做法是mock掉当前类的所有外部依赖,保障自己的代码没有问题。举个例子,如果数据库查询的语句出了问题,单元测试不会测试出来。因为它直接mock掉了,不会去真...
    99+
    2023-09-01
    单元测试 junit java
  • C#使用DLLImport调用外部DLL的方法
    C#.Net调用基本格式: DllImport 属性提供非托管 DLL 函数的调用信息。 [DLLImport(“DLL文件路径”)]修饰符 extern 返...
    99+
    2022-11-13
  • php中为什么无法在外部静态地调用类和方法
    PHP是一种广泛使用的编程语言,以其简洁易用的语法、快速的开发速度和支持多种数据库为特色。但是,有一些PHP开发者可能会遇到一个问题:无法在外部静态地调用类和方法。本文将探讨这个问题,并提供解决方案。什么是静态调用?在PHP开发中,静态调用...
    99+
    2023-05-14
    php
  • java内部类调用的方法是什么
    在Java中,内部类调用的方法可以是外部类的方法,也可以是内部类自身的方法。如果内部类想要调用外部类的方法,可以使用以下语法:```...
    99+
    2023-10-08
    java
  • php子类调用父类的方法有哪些
    在PHP中,子类可以通过以下几种方式调用父类的方法:1. 使用 `parent::methodName()`:使用`parent::...
    99+
    2023-08-11
    php
  • php类中调用其它类的方法有哪些
    这篇文章主要讲解了“php类中调用其它类的方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php类中调用其它类的方法有哪些”吧!一种方法是使用静态方法。静态方法可以在不实例化类的情况...
    99+
    2023-07-05
  • java调用父类的方法有哪些
    在Java中,调用父类的方法有以下几种方式:1. 使用super关键字:使用super关键字可以调用父类的方法。可以通过super....
    99+
    2023-08-09
    java
  • 在动态库中调用外部函数的方法
    本篇内容介绍了“在动态库中调用外部函数的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!愉快的玩耍比如:...
    99+
    2022-10-19
  • c++ string类的常用方法有哪些
    C++中的string类有很多常用的方法,一些常用的方法包括:1. length():返回字符串的长度。2. empty():判断字...
    99+
    2023-10-08
    c++
  • php如何调用文件类的方法
    要调用一个文件类的方法,首先需要先创建一个该类的实例对象,然后通过该对象调用相应的方法。以下是一个示例代码:```php```在上述...
    99+
    2023-10-09
    php
  • php如何调用class类中的方法
    这篇文章主要介绍“php如何调用class类中的方法”,在日常操作中,相信很多人在php如何调用class类中的方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”php如何调用class类中的方法”的疑惑有所...
    99+
    2023-07-02
  • C# Regex类中常用的方法有哪些
    这篇文章主要讲解了“C# Regex类中常用的方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C# Regex类中常用的方法有哪些”吧!Regex 类Regex 类用于表示一个正则表...
    99+
    2023-06-17
  • php如何调用一个类中的方法
    这篇文章主要介绍php如何调用一个类中的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!在PHP中,可以利用“->”来调用一个类中的方法,该符号用于引用类实例的方法和属性,是对象执行方法或取得属性用的,语法为...
    99+
    2023-06-29
  • java如何调用另一个类的方法
    要调用另一个类的方法,首先需要实例化该类的对象,然后通过该对象调用方法。例如,有一个名为"AnotherClass"的类,其中有一个...
    99+
    2023-08-09
    java
  • PHP一个类调用另一个类的方法如何实现
    本篇内容主要讲解“PHP一个类调用另一个类的方法如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PHP一个类调用另一个类的方法如何实现”吧!在面向对象编程中,类与类之间的依赖关系很常见。当...
    99+
    2023-07-06
  • Spring @Cacheable注解类内部调用失效的解决方法
    这期内容当中小编将会给大家带来有关Spring @Cacheable注解类内部调用失效的解决方法,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。@Cacheable注解类内部调用失效如果你只是想使...
    99+
    2023-06-22
  • php如何调用类的方法?步骤讲解
    php如何调用类的方法?下面本篇文章就来给大家介绍一下调用类的方法需要的步骤,希望对大家有所帮助!在 PHP 中,调用类的方法需要以下几个步骤。1. 定义类在 PHP 中,首先需要定义类,类是一个包含属性和方法的模板,用于实例化对象。以下是...
    99+
    2023-05-14
    php
  • 在同一个类中调用带有@Transactional注解的方法示例
    目录将调用方法和被调用方法分开使用 AspectJ 的方式使用 Spring 提供的 TransactionTemplate使用Spring的AOP功能来保证事务生效。将调...
    99+
    2023-05-18
    类调用@Transactional注解 同类调用@Transactional
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作