NonCopyable: Rewrite of class documentation.

pull/8509/head
Vincent Coubard 2018-10-23 14:34:43 +01:00
parent 29f7d9d508
commit 3fe42b6c0d
1 changed files with 81 additions and 71 deletions

View File

@ -24,88 +24,96 @@
namespace mbed { namespace mbed {
/** /**
* Inheriting from this class autogeneration of copy construction and copy * Prevents generation of copy constructor and copy assignment operator in
* assignment operations. * derived classes.
* *
* Classes which are not value type should inherit privately from this class * @par Usage
* to avoid generation of invalid copy constructor or copy assignment operator *
* which can lead to unnoticeable programming errors. * To prevent generation of copy constructor and copy assignment operator simply
* * inherit privately from the NonCopyable class.
* As an example consider the following signature: *
* * @code
* class Resource : NonCopyable<Resource> { };
*
* Resource r;
* // generates compile time error:
* Resource r2 = r;
* @endcode
*
* @par Background information
*
* Instances of polymorphic classes are not meant to be copied. Unfortunately,
* the C++ standards generates a default copy constructor and copy assignment
* function if these functions have not been defined in the class.
*
* Consider the following example:
*
* @code * @code
* class Resource; * // base class representing a connection
* * struct Connection {
* class Foo { * Connection();
* virtual ~Connection();
* virtual void open() = 0;
* }
*
* class SerialConnection : public Connection {
* public: * public:
* Foo() : _resource(new Resource()) { } * SerialConnection(Serial*);
* ~Foo() { delete _resource; } *
* private: * private:
* Resource* _resource; * Serial* _serial;
* };
*
* Connection& get_connection() {
* static SerialConnection serial_connection;
* return serial_connection;
* } * }
* *
* Foo get_foo(); * Connection connection = get_connection();
*
* Foo foo = get_foo();
* @endcode * @endcode
* *
* There is a bug in this function, it returns a temporary value which will be * There is a subtile bug in this code, the function get_connection returns a
* byte copied into foo then destroyed. Unfortunately, internally the Foo class * reference to a Connection which is captured by value instead of reference.
* manage a pointer to a Resource object. This pointer will be released when the *
* temporary is destroyed and foo will manage a pointer to an already released * When the reference returned by get_connection is copied into connection, the
* Resource. * vtable and others members defined in Connection are copied but members defined
* * in SerialConnection are left apart. This can cause severe crashes or bugs if
* Two issues has to be fixed in the example above: * the virtual functions captured uses members not present in the base
* - Function signature has to be changed to reflect the fact that Foo * declaration.
* instances cannot be copied. In that case accessor should return a *
* reference to give access to objects already existing and managed. * To solve that problem, the copy constructor and assignment operator have to
* Generator on the other hand should return a pointer to the created object. * be declared (but doesn't need to be defined) in the private section of the
* * Connection class:
*
* @code * @code
* // return a reference to an already managed Foo instance * struct Connection {
* Foo& get_foo();
* Foo& foo = get_foo();
*
* // create a new Foo instance
* Foo* make_foo();
* Foo* m = make_foo();
* @endcode
*
* - Copy constructor and copy assignment operator has to be made private
* in the Foo class. It prevents unwanted copy of Foo objects. This can be
* done by declaring copy constructor and copy assignment in the private
* section of the Foo class.
*
* @code
* class Foo {
* public:
* Foo() : _resource(new Resource()) { }
* ~Foo() { delete _resource; }
* private: * private:
* // disallow copy operations * Connection(const Connection&);
* Foo(const Foo&); * Connection& operator=(const Connection&);
* Foo& operator=(const Foo&);
* // data members
* Resource* _resource;
* } * }
* @endcode * @endcode
* *
* Another solution is to inherit privately from the NonCopyable class. * While manually declaring private copy constructor and assignment functions
* It reduces the boiler plate needed to avoid copy operations but more * works, it is not ideal as these declarations are usually not immediately
* importantly it clarifies the programmer intent and the object semantic. * visible, easy to forget and may be obscure for uninformed programmer.
* *
* class Foo : private NonCopyable<Foo> { * Using the NonCopyable class reduce the boilerplate required and express
* public: * clearly the intent as class inheritance appears right after the class name
* Foo() : _resource(new Resource()) { } * declaration.
* ~Foo() { delete _resource; } *
* private: * @code
* Resource* _resource; * struct Connection : private NonCopyable<Connection> {
* // regular declarations
* } * }
* * @endcode
* @tparam T The type that should be made non copyable. It prevent cases where *
* the empty base optimization cannot be applied and therefore ensure that the *
* cost of this semantic sugar is null. * @par Implementation details
* *
* Using a template type prevents cases where the empty base optimization cannot
* be applied and therefore ensure that the cost of the NonCopyable semantic
* sugar is null.
*
* As an example, the empty base optimization is prohibited if one of the empty * As an example, the empty base optimization is prohibited if one of the empty
* base class is also a base type of the first non static data member: * base class is also a base type of the first non static data member:
* *
@ -142,6 +150,8 @@ namespace mbed {
* // kind of A. sizeof(C) == sizeof(B) == sizeof(int). * // kind of A. sizeof(C) == sizeof(B) == sizeof(int).
* @endcode * @endcode
* *
* @tparam T The type that should be made non copyable.
*
* @note Compile time errors are disabled if the develop or the release profile * @note Compile time errors are disabled if the develop or the release profile
* is used. To override this behavior and force compile time errors in all profile * is used. To override this behavior and force compile time errors in all profile
* set the configuration parameter "platform.force-non-copyable-error" to true. * set the configuration parameter "platform.force-non-copyable-error" to true.