Визуальная студия 2015 — Как подделать & quot; видимость класса & quot; (не из функций) в C ++?

Есть нет функции, которая контролирует видимость / доступность класса в C ++.

Есть ли способ подделать это?
Есть ли какой-нибудь макрос / шаблон / магия C ++, которая может имитировать ближайшее поведение?

Вот ситуация

util.h (библиотека)

class Util{
//note: by design, this Util is useful only for B and C
//Other classes should not even see "Util"public: static void calculate(); //implementation in Util.cpp
};

B.h (библиотека)

#include "Util.h"class B{ /* ... complex thing */  };

C.h (библиотека)

#include "Util.h"class C{ /* ... complex thing */  };

D.h (Пользователь)

#include "B.h"    //<--- Purpose of #include is to access "B", but not "Util"class D{
public: static void a(){
Util::calculate();   //<--- should compile error
//When ctrl+space, I should not see "Util" as a choice.
}
};

Мое плохое решение

Сделать всех членов Util чтобы быть частным, тогда объявите:

friend class B;
friend class C;

(Редактировать: Поблагодарить A.S.H для «здесь не требуется предварительное объявление».)

Недостаток :-

  • Это модифицирующая Util как-то распознать B а также C,
    Это не имеет смысла, на мой взгляд.
  • Теперь B и C могут получить доступ к каждому члену Utilсломай любой private охранник доступа.
    Есть способ включить друга только для некоторых участников но это не так мило и непригодно для этого случая.
  • D просто не могу использовать Util, но все еще могу это увидеть.
    Util все еще выбор при использовании автозаполнения (например, Ctrl + Space) в D.h,

(Изменить) Примечание: Все дело в удобстве для кодирования; для предотвращения ошибок или неправильного использования / лучшего автозаполнения / лучшей инкапсуляции. Речь идет не о противодействии взлому или предотвращении несанкционированного доступа к функции.

(Изменить, принято):

К сожалению, я могу принять только одно решение, поэтому я субъективно выбрал тот, который требует меньше работы и обеспечивает большую гибкость.

Для будущих читателей, Приет Кукрети (& texasbruce в комментарии) и Шмуэль Х. (& A.S.H Это комментарий) также предоставил хорошие решения, которые стоит прочитать.

8

Решение

Одним из возможных решений было бы засунуть Util в пространство имен, и typedef это внутри B а также C классы:

namespace util_namespace {

class Util{
public:
static void calculate(); //implementation in Util.cpp
};
};

class B {

typedef util_namespace::Util Util;

public:

void foo()
{
Util::calculate(); // Works
}
};

class C {

typedef util_namespace::Util Util;

public:

void foo()
{
Util::calculate(); // Works
}
};

class D {

public:

void foo()
{
Util::calculate(); // This will fail.
}
};

Если Util класс реализован в util.cpp, это потребует обернуть его внутри namespace util_namespace { ... }, Так далеко как B а также C обеспокоены тем, что их реализация может ссылаться на класс с именем Utilи никто не станет мудрее. Без включения typedef, D не найдет класс с таким именем.

3

Другие решения

Я думаю, что лучший способ не включать Util.h в публичном заголовке вообще.

Чтобы сделать это, #include "Util.h" только в реализации cpp файл:

Lib.cpp:

#include "Util.h"
void A::publicFunction()
{
Util::calculate();
}

Делая это, вы убедитесь, что изменение Util.h будет иметь значение только в ваших файлах библиотеки, а не в пользователях библиотеки.

Проблема с этим подходом заключается в том, что не сможет использовать Util в ваших публичных заголовках (A.h, B.h). Форвард-объявление может быть частичным решением этой проблемы:

// Forward declare Util:
class Util;

class A {
private:
// OK;
Util *mUtil;

// ill-formed: Util is an incomplete type
Util mUtil;
}
5

Один из способов сделать это — создать единый класс-посредник, единственная цель которого — предоставить интерфейс доступа к базовым функциям. Это требует немного шаблонного. затем A а также B являются подклассами и, следовательно, могут использовать интерфейс доступа, но не напрямую Utils:

class Util
{
private:
// private everything.
static int utilFunc1(int arg) { return arg + 1; }
static int utilFunc2(int arg) { return arg + 2; }

friend class UtilAccess;
};

class UtilAccess
{
protected:
int doUtilFunc1(int arg) { return Util::utilFunc1(arg); }
int doUtilFunc2(int arg) { return Util::utilFunc2(arg); }
};

class A : private UtilAccess
{
public:
int doA(int arg) { return doUtilFunc1(arg); }
};

class B : private UtilAccess
{
public:
int doB(int arg) { return doUtilFunc2(arg); }
};

int main()
{
A a;
const int x = a.doA(0); // 1
B b;
const int y = b.doB(0); // 2
return 0;
}

ни A или же B иметь доступ к Util непосредственно. Код клиента не может позвонить UtilAccess участники через A или же B экземпляры либо. Добавление дополнительного класса C который использует текущий Util функциональность не потребует модификации Util или же UtilAccess код.

Это означает, что у вас есть более жесткий контроль над Util (особенно если это состояние), что облегчает рассуждение кода, поскольку весь доступ осуществляется через заданный интерфейс, а не дает прямой / случайный доступ к анонимному коду (например, A а также B).

Это требует шаблона и не распространяет автоматически изменения от UtilОднако это более безопасный образец, чем прямая дружба.

Если вы не хотите иметь подкласс, и вы счастливы иметь UtilAccess изменить для каждого класса использования, вы можете внести следующие изменения:

class UtilAccess
{
protected:
static int doUtilFunc1(int arg) { return Util::utilFunc1(arg); }
static int doUtilFunc2(int arg) { return Util::utilFunc2(arg); }

friend class A;
friend class B;
};

class A
{
public:
int doA(int arg) { return UtilAccess::doUtilFunc1(arg); }
};

class B
{
public:
int doB(int arg) { return UtilAccess::doUtilFunc2(arg); }
};

Есть также некоторые связанные решения (для более строгого контроля доступа к частям класса), одно из которых называется Адвокат-клиент а другой называется отмычка, оба обсуждаются в этом ответе: чистый C ++ гранулированный эквивалент друга? (Ответ: идиома адвокат-клиент) . Оглядываясь назад, я думаю, что решение, которое я представил, представляет собой вариант идиомы «адвокат-клиент».

2
По вопросам рекламы [email protected]