Ограничение доступа громко к глобальной переменной, определенной в пространстве имен
У меня есть небольшая проблема, связанная с пространствами имен, учитывая следующее в заголовочном файле:
namespace A {
namespace B {
void SetMemberValue(double value) { _member = value; }
double FunctionThatUsesMember(double a) { return a * _member; }
double AnotherFuncThatUsesMember(double a) { return a / _member; }
static double _member = 0.01;
}
}
Я не хотел, чтобы пользователь мог изменять значение _member
с помощью A::B::_member = some_value
, После прочтения безымянных пространств имен я изменил его на:
namespace A {
namespace B {
void SetMemberValue(double value) { _member = value; }
double FunctionThatUsesMember(double a) { return a * _member; }
double AnotherFuncThatUsesMember(double a) { return a / _member; }
namespace {
double _member = 0.01;
}
}
}
Это заставляет пользователя использовать предоставленную функцию мутатора и прекрасно работает, за исключением одной проблемы:
Если пользователь продолжает использовать: A::B::_member = some_value
код не может скомпилировать, связать или выполнить; утверждение просто игнорируется и значение по умолчанию 0.01
может привести к ошибкам во время выполнения или "OMG WTF IS WRONG BBQ!!1!!" моменты. (Утверждение, что это не так, может быть проблемой с MSVC++ и VS2010, хотя я не уверен.)
Вопрос: Есть ли способ, чтобы код громко провалился, когда A::B::_member = some_value
ошибочно используется?
1 ответ
Прежде всего, обратите внимание, что вы получаете другую версию _member
в каждом переводчике! Я не уверен, является ли это намеренным или нет.
Если вы действительно хотите один _member
в вашей программе, и вы не хотите, чтобы пользователь имел доступ к определенной глобальной переменной, вы не должны делать это видимым в заголовке! Поместите его в источник и предоставьте функции для доступа к нему:
// some-module.h
double getValue();
void setValue(double value);
// some-module.cpp
#include "some-module.h"
static double value(0.01);
double getValue() { return value; }
void setValue(double value) { ::value = value; }
Я пропустил пространства имен, потому что они на самом деле не имеют значения. Вы можете использовать безымянное пространство имен вместо static
внутри блока перевода, но это не имеет большого значения, на самом деле.
Если вы утверждаете, что дополнительный вызов функции недопустим и все должно быть в заголовке, вы можете сделать значение закрытым членом класса. Вы все еще должны были бы обернуть его в функцию, чтобы избежать дублирования символов. Если вы также поместите класс в безымянное пространство имен, вы также можете иметь одну версию значения на единицу перевода:
#if ONE_VALUE_PER_TRANSLATION_UNIT
namespace {
#endif
class Value
{
static double& value() { static double rc(0.01); return rc; }
friend double getValue();
friend void setValue(double value);
};
double getValue() { return Value::value(); }
void setValue(double value) { Value::value() = value; }
#if ONE_VALUE_PER_TRANSLATION_UNIT
}
#endif
Очевидно, что вы можете добавить больше функций для доступа к значению во всех этих случаях. Я только что продемонстрировал доступ с помощью простой функции, не являющейся членом getValue()
а также setValue()
, То, что вы действительно выставляете, зависит от вас.