Что такое прокси-класс в C ++ - PullRequest
39 голосов
/ 15 июня 2009

Что такое прокси-класс в C ++? Почему он создан и где он полезен?

Ответы [ 3 ]

67 голосов
/ 15 июня 2009

Прокси - это класс, который предоставляет модифицированный интерфейс другому классу. Вот пример - предположим, у нас есть класс массива, который мы хотим, чтобы мы могли содержать только двоичные цифры 1 или 0. Вот первая попытка:

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

Мы хотим, чтобы оператор [] жаловался, если мы говорим что-то вроде a [1] = 42, но это невозможно, поскольку оператор видит только индекс в массиве, а не сохраняемое значение.

Мы можем решить это с помощью прокси:

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

Теперь прокси-класс проверяет двоичную цифру, и мы заставляем оператор массива [] возвращать экземпляр прокси, который имеет ограниченный доступ к внутренним объектам массива.

13 голосов
/ 16 декабря 2013

Прокси-класс в C ++ используется для реализации Прокси-шаблона , в котором объект является интерфейсом или посредником для какого-либо другого объекта.

Типичное использование прокси-класса в C ++ - реализация оператора [], поскольку оператор [] может использоваться для получения данных или для установки данных внутри объекта. Идея состоит в том, чтобы предоставить прокси-класс, который позволяет обнаруживать использование данных получения оператором [] по сравнению с использованием набора данных оператора []. Оператор [] класса использует прокси-объект, чтобы помочь сделать правильные вещи, определяя, используется ли оператор [] для получения или установки данных в объекте.

Компилятор C ++ выбирает подходящие операторы и операторы преобразования из предоставленного целевого класса и определений прокси-классов для конкретного использования оператора [].

Однако существуют и другие варианты использования прокси-класса в C ++. Например, см. Эту статью о Самостоятельной регистрации объектов в C ++ от доктора Доббса, в которой описывается использование прокси-класса в качестве части фабрики объектов. Фабрика объектов предоставляет определенный тип объекта в зависимости от некоторых критериев, в этом примере формат графического изображения. Каждый из различных конвертеров графических изображений представлен прокси-объектом.

Все эти требования могут быть выполнены с помощью «специализированного магазина» в которого нет ни одного места в коде во время компиляции, который знает обо всех поддерживаемых форматах. Список поддерживаемых объектов построен на время выполнения, когда каждый объект формата файла регистрирует свое существование с объект специализированного магазина.

Создание специализированного магазина состоит из четырех частей:

  • Каждый класс, который идет в хранилище, будет представлен прокси-классом. Прокси знает, как создавать объекты для магазина и предоставляет стандартный интерфейс для информации о классе.
  • Вы должны решить, какие критерии специализированный магазин будет предоставлять звонящим, а затем реализовать интерфейсы для этих критериев в хранилище, в класс прокси и в исходном классе.
  • Все прокси-классы будут производными от общего базового класса, чтобы специализированный магазин мог использовать их взаимозаменяемо. Каждый прокси-класс будет реализован в виде шаблона, который вызывает статические функции в оригинале класс.
  • Прокси-классы будут регистрироваться автоматически при запуске программы путем определения глобальной переменной для каждого прокси-класса, конструктор которого зарегистрирует прокси-класс в специализированном магазине.

См. Также этот ответ https://stackoverflow.com/a/53253728/1466970, на вопрос об итераторах C ++, в которых прокси-класс используется для представления в качестве уникального объекта каждого из членов массива структуры. Структура является резидентной базой данных для встроенного приложения. Несколько различных видов мнемоники хранятся в виде текстовых символьных массивов в резидентной базе данных памяти. Прокси-класс предоставляет представление, которое затем можно использовать с итератором для обхода списка мнемоник в определенной области. Итератор обращается к прокси-объекту через базовый класс и сведения о том, сколько мнемоник представляет прокси-объект, а длина каждого мнемоника находится в самом прокси-объекте.

Другим примером может быть то, как объекты Microsoft DCOM (Distributed COM) используют прокси на хост-компьютере пользователя объекта DCOM для представления фактического объекта, который находится на другом хост-компьютере. Прокси-сервер предоставляет интерфейс для реального объекта на другом компьютере и обрабатывает взаимодействие между пользователем объекта и фактическим объектом.

Подводя итог, прокси-объект используется в качестве посредника для фактического объекта. Прокси-объект используется, когда когда-либо требуется какое-то преобразование или преобразование между пользователем объекта и фактическим объектом с некоторой косвенной направленностью, которая предоставляет услугу, позволяющую использовать фактический объект, когда есть некоторое препятствие в использовании фактический объект напрямую.

EDIT - простой пример использования прокси с оператором [] для простого хранилища данных массива

Следующий источник использует прокси-объект для оператора [] класса. Ниже приведен вывод тестового набора, чтобы показать создание и уничтожение различных прокси-объектов, поскольку прокси-класс используется для доступа к фактическому классу и манипулирования им. Поучительно запустить его в отладчике, чтобы посмотреть, как он выполняется.

// 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
4 голосов
/ 22 июля 2010

A прокси-класс позволяет скрыть приватные данные класса от клиентов этого класса.

Предоставление клиентам вашего класса прокси-класса, который знает только открытый интерфейс вашего класса, позволяет клиентам использовать службы вашего класса, не предоставляя клиенту доступ к деталям реализации вашего класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...