|
24 | 24 | namespace mbed {
|
25 | 25 |
|
26 | 26 | /**
|
27 |
| - * Inheriting from this class autogeneration of copy construction and copy |
28 |
| - * assignment operations. |
29 |
| - * |
30 |
| - * Classes which are not value type should inherit privately from this class |
31 |
| - * to avoid generation of invalid copy constructor or copy assignment operator |
32 |
| - * which can lead to unnoticeable programming errors. |
33 |
| - * |
34 |
| - * As an example consider the following signature: |
35 |
| - * |
| 27 | + * Prevents generation of copy constructor and copy assignment operator in |
| 28 | + * derived classes. |
| 29 | + * |
| 30 | + * @par Usage |
| 31 | + * |
| 32 | + * To prevent generation of copy constructor and copy assignment operator simply |
| 33 | + * inherit privately from the NonCopyable class. |
| 34 | + * |
| 35 | + * @code |
| 36 | + * class Resource : NonCopyable<Resource> { }; |
| 37 | + * |
| 38 | + * Resource r; |
| 39 | + * // generates compile time error: |
| 40 | + * Resource r2 = r; |
| 41 | + * @endcode |
| 42 | + * |
| 43 | + * @par Background information |
| 44 | + * |
| 45 | + * Instances of polymorphic classes are not meant to be copied. Unfortunately, |
| 46 | + * the C++ standards generates a default copy constructor and copy assignment |
| 47 | + * function if these functions have not been defined in the class. |
| 48 | + * |
| 49 | + * Consider the following example: |
| 50 | + * |
36 | 51 | * @code
|
37 |
| - * class Resource; |
38 |
| - * |
39 |
| - * class Foo { |
| 52 | + * // base class representing a connection |
| 53 | + * struct Connection { |
| 54 | + * Connection(); |
| 55 | + * virtual ~Connection(); |
| 56 | + * virtual void open() = 0; |
| 57 | + * } |
| 58 | + * |
| 59 | + * class SerialConnection : public Connection { |
40 | 60 | * public:
|
41 |
| - * Foo() : _resource(new Resource()) { } |
42 |
| - * ~Foo() { delete _resource; } |
| 61 | + * SerialConnection(Serial*); |
| 62 | + * |
43 | 63 | * private:
|
44 |
| - * Resource* _resource; |
| 64 | + * Serial* _serial; |
| 65 | + * }; |
| 66 | + * |
| 67 | + * Connection& get_connection() { |
| 68 | + * static SerialConnection serial_connection; |
| 69 | + * return serial_connection; |
45 | 70 | * }
|
46 | 71 | *
|
47 |
| - * Foo get_foo(); |
48 |
| - * |
49 |
| - * Foo foo = get_foo(); |
50 |
| - * @endcode |
51 |
| - * |
52 |
| - * There is a bug in this function, it returns a temporary value which will be |
53 |
| - * byte copied into foo then destroyed. Unfortunately, internally the Foo class |
54 |
| - * manage a pointer to a Resource object. This pointer will be released when the |
55 |
| - * temporary is destroyed and foo will manage a pointer to an already released |
56 |
| - * Resource. |
57 |
| - * |
58 |
| - * Two issues has to be fixed in the example above: |
59 |
| - * - Function signature has to be changed to reflect the fact that Foo |
60 |
| - * instances cannot be copied. In that case accessor should return a |
61 |
| - * reference to give access to objects already existing and managed. |
62 |
| - * Generator on the other hand should return a pointer to the created object. |
63 |
| - * |
64 |
| - * @code |
65 |
| - * // return a reference to an already managed Foo instance |
66 |
| - * Foo& get_foo(); |
67 |
| - * Foo& foo = get_foo(); |
68 |
| - * |
69 |
| - * // create a new Foo instance |
70 |
| - * Foo* make_foo(); |
71 |
| - * Foo* m = make_foo(); |
| 72 | + * Connection connection = get_connection(); |
72 | 73 | * @endcode
|
73 |
| - * |
74 |
| - * - Copy constructor and copy assignment operator has to be made private |
75 |
| - * in the Foo class. It prevents unwanted copy of Foo objects. This can be |
76 |
| - * done by declaring copy constructor and copy assignment in the private |
77 |
| - * section of the Foo class. |
78 |
| - * |
| 74 | + * |
| 75 | + * There is a subtile bug in this code, the function get_connection returns a |
| 76 | + * reference to a Connection which is captured by value instead of reference. |
| 77 | + * |
| 78 | + * When the reference returned by get_connection is copied into connection, the |
| 79 | + * vtable and others members defined in Connection are copied but members defined |
| 80 | + * in SerialConnection are left apart. This can cause severe crashes or bugs if |
| 81 | + * the virtual functions captured uses members not present in the base |
| 82 | + * declaration. |
| 83 | + * |
| 84 | + * To solve that problem, the copy constructor and assignment operator have to |
| 85 | + * be declared (but doesn't need to be defined) in the private section of the |
| 86 | + * Connection class: |
| 87 | + * |
79 | 88 | * @code
|
80 |
| - * class Foo { |
81 |
| - * public: |
82 |
| - * Foo() : _resource(new Resource()) { } |
83 |
| - * ~Foo() { delete _resource; } |
| 89 | + * struct Connection { |
84 | 90 | * private:
|
85 |
| - * // disallow copy operations |
86 |
| - * Foo(const Foo&); |
87 |
| - * Foo& operator=(const Foo&); |
88 |
| - * // data members |
89 |
| - * Resource* _resource; |
| 91 | + * Connection(const Connection&); |
| 92 | + * Connection& operator=(const Connection&); |
90 | 93 | * }
|
91 | 94 | * @endcode
|
92 |
| - * |
93 |
| - * Another solution is to inherit privately from the NonCopyable class. |
94 |
| - * It reduces the boiler plate needed to avoid copy operations but more |
95 |
| - * importantly it clarifies the programmer intent and the object semantic. |
96 |
| - * |
97 |
| - * class Foo : private NonCopyable<Foo> { |
98 |
| - * public: |
99 |
| - * Foo() : _resource(new Resource()) { } |
100 |
| - * ~Foo() { delete _resource; } |
101 |
| - * private: |
102 |
| - * Resource* _resource; |
| 95 | + * |
| 96 | + * While manually declaring private copy constructor and assignment functions |
| 97 | + * works, it is not ideal as these declarations are usually not immediately |
| 98 | + * visible, easy to forget and may be obscure for uninformed programmer. |
| 99 | + * |
| 100 | + * Using the NonCopyable class reduce the boilerplate required and express |
| 101 | + * clearly the intent as class inheritance appears right after the class name |
| 102 | + * declaration. |
| 103 | + * |
| 104 | + * @code |
| 105 | + * struct Connection : private NonCopyable<Connection> { |
| 106 | + * // regular declarations |
103 | 107 | * }
|
104 |
| - * |
105 |
| - * @tparam T The type that should be made non copyable. It prevent cases where |
106 |
| - * the empty base optimization cannot be applied and therefore ensure that the |
107 |
| - * cost of this semantic sugar is null. |
108 |
| - * |
| 108 | + * @endcode |
| 109 | + * |
| 110 | + * |
| 111 | + * @par Implementation details |
| 112 | + * |
| 113 | + * Using a template type prevents cases where the empty base optimization cannot |
| 114 | + * be applied and therefore ensure that the cost of the NonCopyable semantic |
| 115 | + * sugar is null. |
| 116 | + * |
109 | 117 | * As an example, the empty base optimization is prohibited if one of the empty
|
110 | 118 | * base class is also a base type of the first non static data member:
|
111 | 119 | *
|
@@ -142,6 +150,8 @@ namespace mbed {
|
142 | 150 | * // kind of A. sizeof(C) == sizeof(B) == sizeof(int).
|
143 | 151 | * @endcode
|
144 | 152 | *
|
| 153 | + * @tparam T The type that should be made non copyable. |
| 154 | + * |
145 | 155 | * @note Compile time errors are disabled if the develop or the release profile
|
146 | 156 | * is used. To override this behavior and force compile time errors in all profile
|
147 | 157 | * set the configuration parameter "platform.force-non-copyable-error" to true.
|
|
0 commit comments