C ++中的代理类是什么?为什么创建它以及它在哪里有用?
答案 0 :(得分:67)
代理是一个为另一个类提供修改过的接口的类。这是一个例子 - 假设我们有一个数组类,我们只希望能够包含二进制数字1或0.这是第一次尝试:
struct array1 {
int mArray[10];
int & operator[](int i) {
/// what to put here
}
}; `
如果我们说[1] = 42之类的东西,我们希望operator []抱怨,但这是不可能的,因为运算符只能看到数组的索引,而不是存储的值。
我们可以使用代理来解决这个问题:
#include <iostream>
using namespace std;
struct aproxy {
aproxy(int& r) : mPtr(&r) {}
void operator = (int n) {
if (n > 1) {
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;
}
}
代理类现在检查二进制数字,我们让数组的operator []返回代理的一个实例,该实例对数组内部的访问权限有限。
答案 1 :(得分:13)
C ++中的代理类用于实现Proxy Pattern,其中对象是接口或其他对象的中介。
C ++中代理类的典型用法是实现[]运算符,因为[]运算符可用于获取数据或在对象中设置数据。我们的想法是提供一个代理类,它允许检测[]运算符的get数据使用与[]运算符的set数据使用的检测。类的[]运算符使用代理对象通过检测[]运算符是否用于获取或设置对象中的数据来帮助做正确的事情。
C ++编译器从提供的目标类和代理类定义中选择适当的运算符和转换运算符,以便特定地使用[]运算符。
但是,C ++中的代理类还有其他用途。例如,请参阅Dobbs博士的Self-Registering Objects in C++上的这篇文章,其中描述了使用代理类作为对象工厂的一部分。对象工厂根据某些标准提供特定类型的对象,在此示例中为图形图像格式。每个不同的图形图像转换器由代理对象表示。
所有这些要求都可以通过使用“专卖店”来满足 在编译时代码中没有单个位置知道 关于所有支持的格式。支持的对象列表是在 运行时每个文件格式对象用a注册它的存在 专卖店对象。
建立专卖店有四个部分:
- 商店中的每个类都将由代理类表示。代理知道如何为商店创建对象 提供有关该课程信息的标准界面。
- 您必须确定专卖店向呼叫者公开的标准,然后在商店中为这些标准实施接口 代理类,以及原始类。
- 所有代理类都派生自一个公共基类,以便专卖店可以互换使用它们。每个代理类都是 实现为调用原始函数的模板 类。
- 通过为构造函数的每个代理类定义一个全局变量,将在程序启动时自动注册代理类 将在专卖店注册代理课程。
另请参阅此答案,https://stackoverflow.com/a/53253728/1466970,关于C ++迭代器的问题,其中代理类用于表示结构的每个数组成员的唯一对象。该结构是嵌入式应用程序的内存驻留数据库。几种不同类型的助记符作为文本字符数组存储在内存驻留数据库中。代理类提供了一种表示,然后可以与迭代器一起使用来遍历特定区域中的助记符列表。迭代器通过基类访问代理对象,并了解代理对象表示的助记符数以及代理对象本身中每个助记符的长度。
另一个例子是Microsoft DCOM(分布式COM)对象如何在DCOM对象的用户的主机上使用代理来表示驻留在另一台主机上的实际对象。代理为不同机器上的实际对象提供接口,并处理对象用户与实际对象之间的通信。
总而言之,代理对象用作实际对象的中介。当需要在对象的用户和具有某种间接的实际对象之间进行某种转换或转换时使用代理对象,该间接提供允许在使用中存在某些障碍时使用实际对象的服务实际的对象直接。
编辑 - 使用带有operator []的代理的简单示例
以下源代码使用代理对象作为类的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;
}
以下是来自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
答案 2 :(得分:4)
代理类允许您从类的客户端隐藏类的私有数据。
为您的类的客户提供仅知道您的类的公共接口的代理类,使客户端能够使用您的类的服务,而无需客户端访问您的类的实现细节。