Есть ли у частного нового оператора неожиданные побочные эффекты?

В этом блоге я читал, что закрытие нового оператора является хорошим подходом для принудительного создания экземпляра в стеке.

Я реализую класс, который использует идиому RAII. Этот класс, очевидно, должен создаваться только в стеке, поэтому я ищу способ обеспечить это.

  • У меня вопрос, есть ли у этого побочные эффекты, которые не так просто увидеть?
  • Это хороший подход для обеспечения реализации в стеке?
  • Есть ли проблемы с переносимостью?

Спасибо за вашу помощь!

РЕДАКТИРОВАТЬ

Мой класс RAII просто создает экземпляры различных частей фреймворка, над которым я работаю, поэтому нет смысла делать что-либо еще с этим классом, кроме создания экземпляра в стеке.

Цель состоит в том, чтобы просто предоставить простую возможность сконфигурировать инфраструктуру и перевести ее в состояние готовности к использованию, без необходимости создания 10 объектов в клиентском коде.

3 ответа

Решение

Я реализую класс, который использует идиому RAII. Этот класс, очевидно, должен создаваться только в стеке, поэтому я ищу способ обеспечить это.

Это не RAII. RAII инициализируется при построении и деинициализации в деструкторах. RAII - это ресурсы, которыми вы управляете (например, члены данных), а не время жизни "родительского" объекта. Пользователи вашего типа затем снова применяют RAII для управления временем жизни того, что они выделяют. Например, вы можете динамически выделить std::vector, даже если vector использует RAII, чтобы убедиться, что каждый "принадлежащий" элемент очищен.


У меня вопрос, есть ли у этого побочные эффекты, которые не так просто увидеть?

Да, вы запрещаете использование вашего типа (по крайней мере, в отношении RAII).

void example() {
  shared_ptr<T> p (new T());
  // No RAII violation, still uses operator new.

  vector<T> v (some_initial_size);
  // No RAII violation, still uses operator new (the placement form is used
  // inside the default allocator).
}

Это хороший подход для обеспечения реализации в стеке?

Что вы пытаетесь предотвратить? Какой вариант использования? Любой, кто использует new с вашим типом, либо 1) знает, что он делает, и нуждается в нем, либо 2) ужасно испортит управление ресурсами независимо от того, что вы делаете. Вы мешаете тем, кто находится в #1, не помогая тем, кто находится в #2, пытаясь применить это.

Это еще один способ заявить, что сказал Стив:

Этот класс, очевидно, должен создаваться только в стеке, поэтому я ищу способ обеспечить это.

Если это очевидно, почему это необходимо применять?

Если пользователи будут слепо использовать "new T()", что заставляет вас думать, что они не будут слепо использовать "::new T()"?


Цель состоит в том, чтобы просто предоставить простую возможность сконфигурировать среду и перевести ее в состояние готовности к использованию, не создавая экземпляров 10 объектов в клиентском коде.

#include <framework>

int main() {
  framework::Init init;
  // Do stuff.
}

Используйте это, или что-то очень близкое к нему, заметно в примерах ваших документов.

Этот класс, очевидно, должен создаваться только в стеке, поэтому я ищу способ обеспечить это.

Я предполагаю, что это не будет очевидно для пользователей, если вам придется применять это...

Основным недостатком является то, что это не работает.

Даже если новые операторы являются частными, пользователи все равно могут случайно использовать ваш класс в качестве члена данных или базового класса своего собственного класса, а затем создать экземпляр своего класса с помощью new, Это мешает им писать Foo *f = new Foo(); когда они должны написать Foo f;, но это не гарантирует, что их использование вашего класса RAII соответствует лексической области видимости, что, вероятно, вы и хотели бы применить.

Если кто-то хочет использовать блокировку (или что-то еще) с динамическим сроком хранения, то он либо очень умен, либо действительно глуп. В любом случае, если вы позволите им создать его самостоятельно, вы не сможете остановить их от вашего ограничения.

Что вы могли бы сделать, это сделать все конструкторы приватными (включая ctor-копию), а затем предоставить функцию Friend, которая возвращает значение. Они должны написать:

const Foo &f = GimmeAFoo();

который требует стандарт, продлит срок жизни возвращаемого значения до объема f, Так как они не могут скопировать объект, они не могут заставить значение выйти за рамки ссылки. Поскольку они не могут инициализировать элемент данных или базовый класс, они не могут использовать обходной путь. Обратите внимание, что они должны использовать константную ссылку, поэтому она не работает, если вашему классу нужны неконстантные члены.

Они все еще могут делать что-то глупое, например, инициализировать элемент справочных данных const своего собственного класса, как этот, но сам объект не выходит за рамки, в которых это происходит. У них останется свисающая ссылка, как если бы они взяли указатель на то, чего не должны делать.

Даже если вы не можете запретить другим делать класс RAII базовым классом или членом динамически создаваемого объекта, как сказал Стив, и вы всегда должны информировать пользователей о предполагаемом использовании этого класса, все равно будет хорошей идеей сделать новый Частный оператор, чтобы предотвратить наиболее очевидные злоупотребления.

Поскольку это все допустимый код C++, проблем с переносимостью нет.

Другие вопросы по тегам