Skip to content

Commit 3fe42b6

Browse files
committed
NonCopyable: Rewrite of class documentation.
1 parent 29f7d9d commit 3fe42b6

File tree

1 file changed

+81
-71
lines changed

1 file changed

+81
-71
lines changed

platform/NonCopyable.h

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -24,88 +24,96 @@
2424
namespace mbed {
2525

2626
/**
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+
*
3651
* @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 {
4060
* public:
41-
* Foo() : _resource(new Resource()) { }
42-
* ~Foo() { delete _resource; }
61+
* SerialConnection(Serial*);
62+
*
4363
* private:
44-
* Resource* _resource;
64+
* Serial* _serial;
65+
* };
66+
*
67+
* Connection& get_connection() {
68+
* static SerialConnection serial_connection;
69+
* return serial_connection;
4570
* }
4671
*
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();
7273
* @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+
*
7988
* @code
80-
* class Foo {
81-
* public:
82-
* Foo() : _resource(new Resource()) { }
83-
* ~Foo() { delete _resource; }
89+
* struct Connection {
8490
* 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&);
9093
* }
9194
* @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
103107
* }
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+
*
109117
* As an example, the empty base optimization is prohibited if one of the empty
110118
* base class is also a base type of the first non static data member:
111119
*
@@ -142,6 +150,8 @@ namespace mbed {
142150
* // kind of A. sizeof(C) == sizeof(B) == sizeof(int).
143151
* @endcode
144152
*
153+
* @tparam T The type that should be made non copyable.
154+
*
145155
* @note Compile time errors are disabled if the develop or the release profile
146156
* is used. To override this behavior and force compile time errors in all profile
147157
* set the configuration parameter "platform.force-non-copyable-error" to true.

0 commit comments

Comments
 (0)