Могу ли я сделать переменную _const с этого момента?
Я использую библиотеку, у которой есть класс с функцией init
, отличной от его конструктора. Каждый раз, когда я делаю новый экземпляр, мне нужно вызвать, например:
MyClass a;
a.init();
Так как init
не const
, это мешает мне создавать экземпляры const (я не могу написать const MyClass a
). Есть ли способ вызвать init, а затем объявить из "here on out" (я думаю, для остальной части области) моя переменная const
?
Это работает, но полагается на не касание исходной переменной:
MyClass dont_touch;
dont_touch.init();
const MyClass & a = dont_touch;
Ответы
Ответ 1
Если вы используете С++ 11, вы можете использовать функцию лямбда
const MyClass ConstantVal = []{
MyClass a;
a.init();
return a;
}();
Это позволяет сохранить инициализацию на месте, не предоставляя внешний доступ к изменяемому объекту.
смотрите также:
http://herbsutter.com/2013/04/05/complex-initialization-for-a-const-variable/
Ответ 2
Вы можете создать класс-оболочку и использовать это вместо.
Если MyClass имеет виртуальный деструктор, вы можете чувствовать себя безопасно, исходя из этого:
class WrapperClass : public MyClass
{
public:
WrapperClass()
{
init(); // Let hope this function doesn't throw
}
};
Или напишите класс, содержащий экземпляр MyClass
class WrapperClass
{
public:
WrapperClass()
{
m_myClass.init(); // Let hope this function doesn't throw
}
operator MyClass&() {return m_myClass;}
operator const MyClass&() const {return m_myClass;}
private:
MyClass m_myClass;
};
Или напишите шаблон, чтобы решить эту общую проблему, используя одно из двух вышеприведенных решений: например.
template <class T> class WrapperClass : public T
{
public:
WrapperClass()
{
T::init();
}
};
typedef WrapperClass<MyClass> WrapperClass;
Ответ 3
Создайте функцию, которая обертывает первые две строки и дает вам объект, который готов к работе.
MyClass makeMyClass()
{
MyClass a;
a.init();
return a;
}
// Now you can construct a const object or non-const object.
const MyClass a = makeMyClass();
MyClass b = makeMyClass();
Обновление
Использование makeMyClass()
включает в себя создание и уничтожение временного объекта каждый раз, когда вызывается функция. Если это становится значительной стоимостью, makeMyClass()
можно изменить на:
MyClass const& makeMyClass()
{
static bool inited = false;
static MyClass a;
if ( !inited )
{
inited = true;
a.init();
}
return a;
}
Это использование, как описано выше, будет продолжать работать. Кроме того, один раз также можно сделать следующее:
const MyClass& c = makeMyClass();
Ответ 4
Вы можете сделать это довольно просто, даже без С++ 11 и lambdas:
const MyClass a;
{
MyClass _a;
_a.init();
std::swap(const_cast<MyClass&>(a), _a);
}
Использование const_cast
, по общему признанию, немного взломанно, но оно ничего не сломает, поскольку const
является довольно слабым спецификатором. В то же время он довольно эффективен, поскольку объект MyClass
заменяется только, а не копируется (наиболее разумные объекты с дорогостоящим копированием должны предоставлять функцию swap
и вводить перегрузку std::swap
).
Без броска для этого потребуется помощник:
struct Construct_Init {
operator MyClass() const
{
MyClass a;
a.init();
return a;
}
};
const MyClass a = Construct_Init();
Это может быть как в функции (структура Construct_Init
не должна быть объявлена в области пространства имен), но она немного длиннее. Копия объекта может быть оптимизирована или не удалена с помощью копирования.
Обратите внимание, что в обоих случаях возвращаемое значение init()
теряется. Если он возвращает логическое значение, где true
является успешным, а false
- ошибкой, лучше:
if(!a.init())
throw std::runtime_error("MyClass init failed");
Или просто обязательно обработайте ошибки соответствующим образом.