mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2025-12-17 12:44:42 +03:00
* 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
This commit is contained in:
committed by
Sergey Zubkov
parent
da61d9ad72
commit
1a88c5a537
@@ -5620,49 +5620,55 @@ The return type of the factory should normally be `unique_ptr` by default; if so
|
|||||||
|
|
||||||
class B {
|
class B {
|
||||||
public:
|
public:
|
||||||
B()
|
B() {
|
||||||
{
|
/* ... */
|
||||||
// ...
|
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
|
||||||
f(); // BAD: virtual call in constructor
|
/* ... */
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void f() = 0;
|
virtual void f() = 0;
|
||||||
|
|
||||||
// ...
|
|
||||||
};
|
};
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
class B {
|
class B {
|
||||||
protected:
|
protected:
|
||||||
B() { /* ... */ } // create an imperfectly initialized object
|
class Token {};
|
||||||
|
|
||||||
virtual void PostInitialize() // to be called right after construction
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
f(); // GOOD: virtual dispatch is safe
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
explicit B(Token) { /* ... */ } // create an imperfectly initialized object
|
||||||
virtual void f() = 0;
|
virtual void f() = 0;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static shared_ptr<T> Create() // interface for creating shared objects
|
static shared_ptr<T> create() // interface for creating shared objects
|
||||||
{
|
{
|
||||||
auto p = make_shared<T>();
|
auto p = make_shared<T>(typename T::Token{});
|
||||||
p->PostInitialize();
|
p->post_initialize();
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void post_initialize() // called right after construction
|
||||||
|
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
|
||||||
};
|
};
|
||||||
|
|
||||||
class D : public B { /* ... */ }; // some derived class
|
class D : public B { // some derived class
|
||||||
|
protected:
|
||||||
|
class Token {};
|
||||||
|
|
||||||
shared_ptr<D> p = D::Create<D>(); // creating a D object
|
public:
|
||||||
|
explicit D(Token) : B{ B::Token{} } {}
|
||||||
|
void f() override { /* ... */ };
|
||||||
|
|
||||||
By making the constructor `protected` we avoid an incompletely constructed object escaping into the wild.
|
protected:
|
||||||
By providing the factory function `Create()`, we make construction (on the free store) convenient.
|
template<class T>
|
||||||
|
friend shared_ptr<T> B::create();
|
||||||
|
};
|
||||||
|
|
||||||
|
shared_ptr<D> p = D::create<D>(); // creating a D object
|
||||||
|
|
||||||
|
`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
|
##### Note
|
||||||
|
|
||||||
@@ -21494,53 +21500,64 @@ Here is an example of the last option:
|
|||||||
|
|
||||||
class B {
|
class B {
|
||||||
public:
|
public:
|
||||||
B() { /* ... */ f(); /* ... */ } // BAD: see Item 49.1
|
B() {
|
||||||
|
/* ... */
|
||||||
|
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
virtual void f() = 0;
|
virtual void f() = 0;
|
||||||
|
|
||||||
// ...
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class B {
|
class B {
|
||||||
protected:
|
protected:
|
||||||
B() { /* ... */ }
|
class Token {};
|
||||||
virtual void post_initialize() // called right after construction
|
|
||||||
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
|
|
||||||
public:
|
public:
|
||||||
|
// constructor needs to be public so that make_shared can access it.
|
||||||
|
// protected access level is gained by requiring a Token.
|
||||||
|
explicit B(Token) { /* ... */ } // create an imperfectly initialized object
|
||||||
virtual void f() = 0;
|
virtual void f() = 0;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static shared_ptr<T> create() // interface for creating objects
|
static shared_ptr<T> create() // interface for creating shared objects
|
||||||
{
|
{
|
||||||
auto p = make_shared<T>();
|
auto p = make_shared<T>(typename T::Token{});
|
||||||
p->post_initialize();
|
p->post_initialize();
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
protected:
|
||||||
|
virtual void post_initialize() // called right after construction
|
||||||
|
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class D : public B { // some derived class
|
class D : public B { // some derived class
|
||||||
|
protected:
|
||||||
|
class Token {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// 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 { /* ... */ };
|
void f() override { /* ... */ };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
D() {}
|
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
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:
|
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`.
|
* 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).
|
* 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.
|
* `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).
|
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).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user