* make the sample in Sd-factory compileable (closes#1488)
- make the sample in Sd-factory compileable
- fixed wrong capitalization: create/Create -> create
- `make_shared` cannot access protected constructors, so made them public. To still have access protection introduced a protected `class Token` in each class. That token can only be created by the class itself (and derived classes) and needs to be passed to the constructor.
- changed order: `public` first, then `protected`
- same sample for C.50 and Sd-factory
- removed spurious "see Item 49.1" as it is unclear what this means
* line length
* tabs -> spaces
* spelling
* input from cubbimew
- added back in Item 49.1
- added link for items as suggested ("in [SuttAlex05](#SuttAlex05)")
* changed link to Item 49.1 to link to C.82
class D : public B { /* ... */ }; // some derived class
class D : public B { // some derived class
protected:
class Token {};
public:
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
protected:
template<classT>
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::Create<D>(); // creating a D object
shared_ptr<D> p = D::create<D>(); // creating a D object
By making the constructor `protected` we avoid an incompletely constructed object escaping into the wild.
By providing the factory function `Create()`, we make construction (on the free store) convenient.
`make_shared` requires that the constructor is public. By requiring a protected `Token` the constructor cannot be publicly called anymore, so we avoid an incompletely constructed object escaping into the wild.
By providing the factory function `create()`, we make construction (on the free store) convenient.
##### Note
@ -21494,53 +21500,64 @@ Here is an example of the last option:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
protected:
D() {}
template<classT>
friend shared_ptr<T> B::Create();
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::Create<D>(); // creating a D object
shared_ptr<D> p = D::create<D>(); // creating a D object
This design requires the following discipline:
* Derived classes such as `D` must not expose a public constructor. Otherwise, `D`'s users could create `D` objects that don't invoke `PostInitialize`.
* Allocation is limited to `operator new`. `B` can, however, override `new` (see Items 45 and 46).
* `D` must define a constructor with the same parameters that `B` selected. Defining several overloads of `Create` can assuage this problem, however; and the overloads can even be templated on the argument types.
* Derived classes such as `D` must not expose a publicly callable constructor. Otherwise, `D`'s users could create `D` objects that don't invoke `post_initialize`.
* Allocation is limited to `operator new`. `B` can, however, override `new` (see Items 45 and 46 in [SuttAlex05](#SuttAlex05)).
* `D` must define a constructor with the same parameters that `B` selected. Defining several overloads of `create` can assuage this problem, however; and the overloads can even be templated on the argument types.
If the requirements above are met, the design guarantees that `PostInitialize` has been called for any fully constructed `B`-derived object. `PostInitialize` doesn't need to be virtual; it can, however, invoke virtual functions freely.
If the requirements above are met, the design guarantees that `post_initialize` has been called for any fully constructed `B`-derived object. `post_initialize` doesn't need to be virtual; it can, however, invoke virtual functions freely.
In summary, no post-construction technique is perfect. The worst techniques dodge the whole issue by simply asking the caller to invoke the post-constructor manually. Even the best require a different syntax for constructing objects (easy to check at compile time) and/or cooperation from derived class authors (impossible to check at compile time).