返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >如何在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://lsjlt.com/news/167225.html(转载时请注明来源链接)

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

猜你喜欢
  • 如何在C++类的外部调用类的私有方法
    目录前言问题技术准备1. pointers to member functions2. The explicit template instantiation3. Passing a...
    99+
    2024-04-02
  • 如何mock当前类的私有方法
    背景 基础知识 mockito单元测试:它的做法是mock掉当前类的所有外部依赖,保障自己的代码没有问题。举个例子,如果数据库查询的语句出了问题,单元测试不会测试出来。因为它直接mock掉了,不会去真...
    99+
    2023-09-01
    单元测试 junit java
  • php无法在外部静态调用类和方法的原因是什么
    这篇文章主要讲解了“php无法在外部静态调用类和方法的原因是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php无法在外部静态调用类和方法的原因是什么”吧!什么是静态调用?在PHP开发中...
    99+
    2023-07-05
  • java的内部类和外部类用法讲解
    目录一、为何使用内部类二、内部类与外部类的联系2.1内部类是一个相对独立的实体,与外部类不是is-a关系2.2内部类可以直接访问外部类的元素,但是外部类不可以直接访问内部类的元素2....
    99+
    2024-04-02
  • Python类与方法的私有化
    1. 创建自己的类学习面向对象的第一步,就是创建一个类。因为类是面向对象的基石。Python类和其他编程语言(Java、C#等)的类差不多,也需要使用class关键字。下面通过一个实际的例子来看一下Python类是如何创建的。本例会创建一个...
    99+
    2023-01-31
    方法 Python
  • C#使用DLLImport调用外部DLL的方法
    C#.Net调用基本格式: DllImport 属性提供非托管 DLL 函数的调用信息。 [DLLImport(“DLL文件路径”)]修饰符 extern 返...
    99+
    2024-04-02
  • 如何在C#中调用外部进程
    这篇文章给大家介绍如何在C#中调用外部进程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。在开始正题之前,我们先来看一看网上比较常见的C#调用外部进程:privatestringRunCmd(stringcommand)...
    99+
    2023-06-18
  • php中为什么无法在外部静态地调用类和方法
    PHP是一种广泛使用的编程语言,以其简洁易用的语法、快速的开发速度和支持多种数据库为特色。但是,有一些PHP开发者可能会遇到一个问题:无法在外部静态地调用类和方法。本文将探讨这个问题,并提供解决方案。什么是静态调用?在PHP开发中,静态调用...
    99+
    2023-05-14
    php
  • c#怎么调用类中的方法
    在 c# 中调用类中的方法,需要先创建类的实例,然后使用实例名称和点运算符访问方法。步骤包括:1. 使用 new 关键字创建类的实例;2. 使用实例名称和点运算符访问方法。 如何在 C...
    99+
    2024-05-12
    c#
  • vbs如何调用外部对像和类型库
    这篇文章将为大家详细讲解有关vbs如何调用外部对像和类型库,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。先举个例子,最近的flash网马调用: var Flashver = ...
    99+
    2023-06-08
  • java父类如何调用子类方法
    在Java中,父类可以调用子类方法的一种方式是使用多态。具体步骤如下:1. 定义一个父类,其中包含一个方法。这个方法可以被子类覆盖。...
    99+
    2023-09-22
    java
  • php子类调用父类的方法有哪些
    在PHP中,子类可以通过以下几种方式调用父类的方法:1. 使用 `parent::methodName()`:使用`parent::...
    99+
    2023-08-11
    php
  • java如何调用类方法
    要调用类方法,可以使用类名直接调用方法,而不需要创建类的实例对象。例如,假设有一个类名为MathUtils,其中有一个静态方法add...
    99+
    2023-08-18
    java
  • java内部类调用的方法是什么
    在Java中,内部类调用的方法可以是外部类的方法,也可以是内部类自身的方法。如果内部类想要调用外部类的方法,可以使用以下语法:```...
    99+
    2023-10-08
    java
  • php如何调用文件类的方法
    要调用一个文件类的方法,首先需要先创建一个该类的实例对象,然后通过该对象调用相应的方法。以下是一个示例代码:```php```在上述...
    99+
    2023-10-09
    php
  • php如何调用class类中的方法
    这篇文章主要介绍“php如何调用class类中的方法”,在日常操作中,相信很多人在php如何调用class类中的方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”php如何调用class类中的方法”的疑惑有所...
    99+
    2023-07-02
  • 如何在JavaScript中使用私有类字段
    如何在JavaScript中使用私有类字段?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。javascript是一种什么语言javascript是一种动态类型、弱...
    99+
    2023-06-14
  • Python_子类调用父类的方法
    1.方式一子类调用父类的方法,包含2中形式的调用。一种形式是在类内部通过继承的方式调用父类的方法,另外一种形式是子类实例化后之后通过继承的方式来调用父类的方法。如下图所示:注意一点,在子类内部通过继承的方式调用父类的属性时,必须要带上sel...
    99+
    2023-01-31
    子类 方法
  • php类中调用其它类的方法有哪些
    这篇文章主要讲解了“php类中调用其它类的方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“php类中调用其它类的方法有哪些”吧!一种方法是使用静态方法。静态方法可以在不实例化类的情况...
    99+
    2023-07-05
  • php如何调用一个类中的方法
    这篇文章主要介绍php如何调用一个类中的方法,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!在PHP中,可以利用“->”来调用一个类中的方法,该符号用于引用类实例的方法和属性,是对象执行方法或取得属性用的,语法为...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作