什么是 C++ 中的代理类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/994488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:18:54  来源:igfitidea点击:

What is Proxy Class in C++

c++design-patternsproxy

提问by mahesh

What is a Proxy Class in C++? Why it is created and where it is useful?

什么是 C++ 中的代理类?为什么创建它以及它在哪里有用?

回答by

A proxy is a class that provides a modified interface to another class.

代理是为另一个类提供修改后的接口的类。

Here is an example - suppose we have an array class that we only want to contain binary digits (1 or 0). Here is a first try:

这是一个例子 - 假设我们有一个数组类,我们只想包含二进制数字(1 或 0)。这是第一次尝试:

struct array1 {
    int mArray[10];
    int & operator[](int i) {
      /// what to put here
    }
}; `

We want operator[]to throw if we say something like a[1] = 42, but that isn't possible because that operator only sees the index of the array, not the value being stored.

operator[]如果我们说类似a[1] = 42,我们想抛出,但这是不可能的,因为该运算符只能看到数组的索引,而不是存储的值。

We can solve this using a proxy:

我们可以使用代理解决这个问题:

#include <iostream>
using namespace std;

struct aproxy {
    aproxy(int& r) : mPtr(&r) {}
    void operator = (int n) {
        if (n > 1 || n < 0) {
            throw "not binary digit";
        }
        *mPtr = n;
    }
    int * mPtr;
};

struct array {
    int mArray[10];
    aproxy operator[](int i) {
        return aproxy(mArray[i]);
    }
};

int main() {
    try {
        array a;
        a[0] = 1;   // ok
        a[0] = 42;  // throws exception
    }
    catch (const char * e) {
        cout << e << endl;
    }
}

The proxy class now does our checking for a binary digit and we make the array's operator[]return an instance of the proxy which has limited access to the array's internals.

代理类现在检查二进制数字,并且我们使数组的operator[]返回成为代理的实例,该实例对数组的内部具有有限的访问权限。

回答by Richard Chambers

A proxy class in C++ is used to implement the Proxy Patternin which an object is an interface or a mediator for some other object.

C++ 中的代理类用于实现代理模式,其中对象是某个其他对象的接口或中介。

A typical use of a proxy class in C++ is implementing the [] operator since the [] operator may be used to get data or to set data within an object. The idea is to provide a proxy class which allows for the detection of a get data use of the [] operator versus the set data use of the [] operator. The [] operator of a class uses the proxy object to assist in doing the right thing by detecting whether the [] operator is being used to get or set data in the object.

C++ 中代理类的典型用途是实现 [] 运算符,因为 [] 运算符可用于获取数据或设置对象内的数据。这个想法是提供一个代理类,它允许检测 [] 运算符的获取数据使用与 [] 运算符的设置数据使用。类的 [] 运算符使用代理对象通过检测 [] 运算符是否用于获取或设置对象中的数据来帮助做正确的事情。

The C++ compiler selects the appropriate operators and conversion operators from the provided target class and the proxy class definitions in order to make a particular use of the [] operator work.

C++ 编译器从提供的目标类和代理类定义中选择适当的运算符和转换运算符,以便对 [] 运算符的特定使用发挥作用。

However there are other uses for a proxy class in C++. For instance see this article on Self-Registering Objects in C++from Dr. Dobbs that describes using a proxy class as part of an object factory. The object factory provides a particular type of object depending on some criteria, in this example a graphics image format. Each of the different graphic image converters is represented by a proxy object.

但是,C++ 中的代理类还有其他用途。例如,请参阅Dobbs 博士关于 C++ 中自注册对象的这篇文章,该文章描述了使用代理类作为对象工厂的一部分。对象工厂根据某些标准提供特定类型的对象,在此示例中为图形图像格式。每个不同的图形图像转换器都由一个代理对象表示。

All of these requirements can be met by using a "specialty store" in which there is no single place in the code at compile time that knows about all supported formats. The list of supported objects is built at run time when each file-format object registers its existence with a specialty-store object.

There are four parts to building a specialty store:

  • Each class that goes in the store will be represented by a proxy class. The proxy knows how to create objects for the store and provides a standard interface for information about the class.
  • You must decide what criteria the specialty store will expose to callers, then implement interfaces for those criteria in the store, in the proxy class, and in the original class.
  • All proxy classes will derive from a common base class so that the specialty store can use them interchangeably. Each proxy class will be implemented as a template that calls static functions in the original class.
  • Proxy classes will be registered automatically at program startup by defining a global variable for each proxy class whose constructor will register the proxy class with the specialty store.

所有这些要求都可以通过使用“专业存储”来满足,其中在编译时代码中没有一个地方知道所有支持的格式。当每个文件格式对象向专卖店对象注册其存在时,支持对象的列表是在运行时构建的。

建立专卖店有四个部分:

  • 进入商店的每个类都将由一个代理类表示。代理知道如何为存储创建对象并提供有关类信息的标准接口。
  • 您必须决定专卖店将向调用者公开什么条件,然后在商店、代理类和原始类中为这些条件实现接口。
  • 所有代理类都将派生自一个公共基类,以便专卖店可以互换使用它们。每个代理类将被实现为调用原始类中的静态函数的模板。
  • 通过为每个代理类定义一个全局变量,代理类将在程序启动时自动注册,其构造函数将向专卖店注册代理类。

See also this answer, https://stackoverflow.com/a/53253728/1466970, to a question about C++ iterators in which a proxy class is used to represent as a unique object each of the array members of a struct. The struct is a memory resident database for an embedded application. Several different kinds of mnemonics are stored as text character arrays in the memory resident database. The proxy class provides a representation which can then be used with an iterator to traverse the list of mnemonics in a particular area. The iterator accesses the proxy object through a base class and the intelligence as to the how many mnemonics the proxy object represents and the length of each mnemonic is in the proxy object itself.

另请参阅此答案,https://stackoverflow.com/a/53253728/1466970,有关 C++ 迭代器的问题,其中使用代理类将结构的每个数组成员表示为唯一对象。该结构体是嵌入式应用程序的内存驻留数据库。几种不同类型的助记符作为文本字符数组存储在内存驻留数据库中。代理类提供了一种表示,然后可以将其与迭代器一起使用以遍历特定区域中的助记符列表。迭代器通过基类和关于代理对象代表多少助记符和每个助记符的长度在代理对象本身中的智能来访问代理对象。

Another example would be how Microsoft DCOM (Distributed COM) objects use a proxy on the host machine of a user of the DCOM object to represent the actual object which resides on another host machine. The proxy provides an interface for the actual object on a different machine and handles the communication between the user of the object and the actual object.

另一个示例是 Microsoft DCOM(分布式 COM)对象如何使用 DCOM 对象用户的主机上的代理来表示驻留在另一台主机上的实际对象。代理为不同机器上的实际对象提供接口,并处理对象的用户和实际对象之间的通信。

To sum up, a proxy object is used to act as an intermediary to the actual object. A proxy object is used when ever there needs to be some kind of conversion or transformation between the user of an object and the actual object with some kind of indirection which provides a service allowing the use of the actual object when there is some obstacle in using the actual object directly.

综上所述,代理对象用于充当实际对象的中介。当需要在对象的用户和具有某种间接性的实际对象之间进行某种转换或转换时使用代理对象直接的实际对象。

EDIT - A simple example using a proxy with operator [] for a simple array data store

编辑 - 使用带有运算符 [] 的代理进行简单数组数据存储的简单示例

The following source uses a proxy object for the operator[] of a class. The output of the test harness is provided below to show the creation and destruction of the various proxy objects as the proxy class is used to access and manipulate the actual class. It is instructive to run this in a debugger to watch it execute.

以下源代码为类的 operator[] 使用代理对象。下面提供了测试工具的输出,以显示各种代理对象的创建和销毁,因为代理类用于访问和操作实际类。在调试器中运行它以观察它的执行是有益的。

// proxy.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string.h>

#include <iostream>

class TArrayProxy;

// The actual class which we will access using a proxy.
class TArray
{
public:
    TArray();
    ~TArray ();

    TArrayProxy operator [] (int iIndex);
    int operator = (TArrayProxy &j);
    void Dump (void);

    char    m_TarrayName[4];     // this is the unique name of a particular object.

    static char TarrayName[4];   // This is the global used to create unique object names

private:
    friend class TArrayProxy;    // allow the proxy class access to our data.
    int iArray[10];              // a simple integer array for our data store
};

// The proxy class which is used to access the actual class.
class TArrayProxy
{
public:
    TArrayProxy(TArray *p = 0, int i=0);
    ~TArrayProxy();

    TArrayProxy & operator = (int i);
    TArrayProxy & operator += (int i);
    TArrayProxy & operator = (TArrayProxy &src);
    operator int ();

    int     iIndex;
    char    m_TarrayproxyName[4];        // this is the unique name of a particular object.

    static char TarrayproxyName[4];      // This is the global used to create unique object names

private:
    TArray *pArray;                      // pointer to the actual object for which we are a proxy.
};

// initialize the object names so as to generate unique object names.
char TArray::TarrayName[4] = {" AA"};
char TArrayProxy::TarrayproxyName[4] = {" PA"};

// Construct a proxy object for the actual object along with which particular
// element of the actual object data store that this proxy will represent.
TArrayProxy::TArrayProxy(TArray *p /* = 0 */, int i /* = 0 */)
{
    if (p && i > 0) {
        pArray = p;
        iIndex = i;
        strcpy (m_TarrayproxyName, TarrayproxyName);
        TarrayproxyName[2]++;
        std::cout << "    Create TArrayProxy " << m_TarrayproxyName << " iIndex = " << iIndex  << std::endl;
    } else {
        throw "TArrayProxy bad p";
    }
}

// The destructor is here just so that we can log when it is hit.
TArrayProxy::~TArrayProxy()
{
    std::cout << "      Destroy TArrayProxy " << m_TarrayproxyName << std::endl;
}

// assign an integer value to a data store element by using the proxy object
// for the particular element of the data store.
TArrayProxy & TArrayProxy::operator = (int i)
{
    pArray->iArray[iIndex] = i;
    std::cout << "      TArrayProxy assign = i " << i << " to " << pArray->m_TarrayName << " using proxy " << m_TarrayproxyName << " iIndex " << iIndex << std::endl;
    return *this;
}

TArrayProxy & TArrayProxy::operator += (int i)
{
    pArray->iArray[iIndex] += i;
    std::cout << "      TArrayProxy add assign += i " << i << " to " << pArray->m_TarrayName << " using proxy " << m_TarrayproxyName << " iIndex " << iIndex << std::endl;
    return *this;
}

// assign an integer value that is specified by a proxy object to a proxy object for a different element.
TArrayProxy & TArrayProxy::operator = (TArrayProxy &src)
{
    pArray->iArray[iIndex] = src.pArray->iArray[src.iIndex];
    std::cout << "      TArrayProxy assign = src " << src.m_TarrayproxyName << " iIndex " << src.iIndex << " to " << m_TarrayproxyName << " iIndex "<< iIndex << " from"  << std::endl;
    return *this;
}

TArrayProxy::operator int ()
{
    std::cout << "      TArrayProxy operator int " << m_TarrayproxyName << " iIndex " << iIndex << " value of " << pArray->iArray[iIndex] << std::endl;
    return pArray->iArray[iIndex];
}



TArray::TArray()
{
    strcpy (m_TarrayName, TarrayName);
    TarrayName[2]++;
    std::cout << "  Create TArray = " << m_TarrayName << std::endl;
    for (int i = 0; i < sizeof(iArray)/sizeof(iArray[0]); i++) { iArray[i] = i; }
}

// The destructor is here just so that we can log when it is hit.
TArray::~TArray()
{
    std::cout << "  Destroy TArray " << m_TarrayName << std::endl;
}

TArrayProxy TArray::operator [] (int iIndex)
{
    std::cout << "  TArray operator [" << iIndex << "] " << m_TarrayName << std::endl;
    if (iIndex > 0 && iIndex <= sizeof(iArray)/sizeof(iArray[0])) {
        // create a proxy object for this particular data store element
        return TArrayProxy(this, iIndex);
    }
    else
        throw "Out of range";
}

int TArray::operator = (TArrayProxy &j)
{
    std::cout << "  TArray operator = " << m_TarrayName << " from" << j.m_TarrayproxyName << " index " << j.iIndex << std::endl;
    return j.iIndex;
}

void TArray::Dump (void)
{
    std::cout << std::endl << "Dump of " << m_TarrayName << std::endl;
    for (int i = 0; i < sizeof(iArray)/sizeof(iArray[0]); i++) {
        std::cout << "  i = " << i << "  value = " << iArray [i] << std::endl;
    }
}

// -----------------    Main test harness follows  ----------------
// we will output the line of code being hit followed by the log of object actions.

int _tmain(int argc, _TCHAR* argv[])
{
    TArray myObj;

    std::cout << std::endl  << "int ik = myObj[3];" << std::endl;
    int ik = myObj[3];
    std::cout << std::endl << "myObj[6] = myObj[4] = 40;" << std::endl;
    myObj[6] = myObj[4] = 40;
    std::cout << std::endl << "myObj[5] = myObj[5];" << std::endl;
    myObj[5] = myObj[5];
    std::cout << std::endl << "myObj[2] = 32;" << std::endl;
    myObj[2] = 32;
    std::cout << std::endl << "myObj[8] += 20;" << std::endl;
    myObj[8] += 20;
    myObj.Dump ();
    return 0;
}

And here is the output of this example from a console application with Visual Studio 2005.

下面是这个例子的输出,来自一个带有 Visual Studio 2005 的控制台应用程序。

  Create TArray =  AA

int ik = myObj[3];
  TArray operator [3]  AA
    Create TArrayProxy  PA iIndex = 3
      TArrayProxy operator int  PA iIndex 3 value of 3
      Destroy TArrayProxy  PA

myObj[6] = myObj[4] = 40;
  TArray operator [4]  AA
    Create TArrayProxy  PB iIndex = 4
      TArrayProxy assign = i 40 to  AA using proxy  PB iIndex 4
  TArray operator [6]  AA
    Create TArrayProxy  PC iIndex = 6
      TArrayProxy assign = src  PB iIndex 4 to  PC iIndex 6 from
      Destroy TArrayProxy  PC
      Destroy TArrayProxy  PB

myObj[5] = myObj[5];
  TArray operator [5]  AA
    Create TArrayProxy  PD iIndex = 5
      TArrayProxy operator int  PD iIndex 5 value of 5
  TArray operator [5]  AA
    Create TArrayProxy  PE iIndex = 5
      TArrayProxy assign = i 5 to  AA using proxy  PE iIndex 5
      Destroy TArrayProxy  PE
      Destroy TArrayProxy  PD

myObj[2] = 32;
  TArray operator [2]  AA
    Create TArrayProxy  PF iIndex = 2
      TArrayProxy assign = i 32 to  AA using proxy  PF iIndex 2
      Destroy TArrayProxy  PF

myObj[8] += 20;
  TArray operator [8]  AA
    Create TArrayProxy  PG iIndex = 8
      TArrayProxy add assign += i 20 to  AA using proxy  PG iIndex 8
      Destroy TArrayProxy  PG

Dump of  AA
  i = 0  value = 0
  i = 1  value = 1
  i = 2  value = 32
  i = 3  value = 3
  i = 4  value = 40
  i = 5  value = 5
  i = 6  value = 40
  i = 7  value = 7
  i = 8  value = 28
  i = 9  value = 9

回答by Pikson

A proxy classallows you to hidethe private data of a class from clients of the class.

一个代理类允许您隐藏在类的客户类的私有数据。

Providing clients of your class with a proxy class that knows only the public interface to your class enables the clients to use your class's services without giving the client access to your class's implementation details.

为您的类的客户提供一个只知道您的类的公共接口的代理类,使客户能够使用您的类的服务,而无需让客户访问您的类的实现细节。