programacion poo otra orientada objetos metodo llamar lista importar herencia ejemplos clases clase c++ c++17 raii

c++ - poo - Impedir que los usuarios creen instancias sin nombre de una clase



poo python 3 (4)

Esta pregunta ya tiene una respuesta aquí:

Para muchas clases de "guardias" de RAII , crear instancias como variables anónimas no tiene ningún sentido:

{ std::lock_guard<std::mutex>{some_mutex}; // Does not protect the scope! // The unnamed instance is immediately destroyed. }

{ scope_guard{[]{ cleanup(); }}; // `cleanup()` is executed immediately! // The unnamed instance is immediately destroyed. }

De este artículo :

Las variables anónimas en C ++ tienen "alcance de expresión", lo que significa que se destruyen al final de la expresión en la que se crean.

¿Hay alguna forma de evitar que el usuario realice una instancia de ellos sin un nombre? ("Prevenir" puede ser demasiado fuerte - "hacerlo muy difícil" también es aceptable).

Puedo pensar en dos posibles soluciones, pero introducen sobrecarga sintáctica en el uso de la clase:

  1. Oculte la clase en un espacio de nombres detail y proporcione una macro.

    namespace detail { class my_guard { /* ... */ }; }; #define SOME_LIB_MY_GUARD(...) / detail::my_guard MY_GUARD_UNIQUE_NAME(__LINE__) {__VA_ARGS__}

    Esto funciona, pero es hackish .

  2. Solo permita que el usuario use la protección a través de una función de orden superior.

    template <typename TArgTuple, typename TF> decltype(auto) with_guard(TArgTuple&& guardCtorArgs, TF&& f) { make_from_tuple<detail::my_guard>(std::forward<TArgTuple>(guardCtorArgs)); f(); }

    Uso:

    with_guard(std::forward_as_tuple(some_mutex), [&] { // ... });

    Esta solución no funciona cuando la inicialización de la clase de guarda tiene una sintaxis "fluida":

    { auto _ = guard_creator() .some_setting(1) .some_setting(2) .create(); }

¿Hay alguna alternativa mejor? Tengo acceso a las características de C ++ 17.


La única forma sensata en la que pienso es en hacer que el usuario pase el resultado de guard_creator::create a algún guard_activator que toma una referencia de valor- guard_activator como parámetro.

De esta manera, el usuario de la clase no tiene más remedio que crear el objeto con un nombre (la opción sensata que harán la mayoría de los desarrolladores) o la new desreferencia (opciones insanas)

por ejemplo, usted dijo en los comentarios que trabaja en un creador de cadena asíncrono sin asignación. Puedo pensar en una API que se parece a esto:

auto token = monad_creator().then([]{...}).then([]{...}).then([]{...}).create(); launch_async_monad(token); //gets token as Token&, the user has no way BUT create this object with a name


La forma canónica de evitar que una clase sea instanciada es haciendo que su constructor sea private . Para obtener realmente una de las instancias deseadas, llame a un método static , que devuelve una referencia a un objeto construido.

class Me { public: static Me &MakeMe() { return Me(); } private: Me(); }; // Me

Esto no ayuda, por supuesto, ¡pero probablemente haga que el programador se detenga!

int main() { Me(); // Invalid Me m; // Invalid Me::MakeMe(); // Valid - but who''d write that? Me m = Me::MakeMe(); } // main()

Sé que esto no es un análogo directo a las instancias de Guard que usted describe, pero ¿quizás podría adaptar el concepto?


Podría usar una herramienta de pelusas extensible como Vera ++ https://bitbucket.org/verateam/vera/wiki/Home le permite aporrear su código, puede crear nuevas reglas usando Python o tcl (prefiero Python)

Un posible flujo sería: después de cada confirmación, su sistema de CI (por ejemplo, Jenkins) ejecutará un trabajo que ejecute Vera ++ y validará dichos descuidos, en caso de que se envíe un correo electrónico al interlocutor.


Si tiene acceso al potencial completo de C ++ 17, puede ampliar la idea de usar una función de fábrica estática en algo útil: elision de copia garantizada hace posible la función de fábrica estática incluso para clases no móviles, y el [[nodiscard] ] los atributos le piden al compilador que emita una advertencia si se ignora el valor de retorno.

class [[nodiscard]] Guard { public: Guard(Guard& other) = delete; ~Guard() { /* do sth. with _ptr */ } static Guard create(void* ptr) { return Guard(ptr); } private: Guard(void* ptr) : _ptr(ptr) {} void* _ptr; }; int main(int, char**) { Guard::create(nullptr); //auto g = Guard::create(nullptr); }

Compilar en el compilador Explorer