如何在C++中实现初始化终结器

How to implement initialization finalizers in C++?

本文关键字:初始化 实现 C++      更新时间:2023-10-16

我想在基类中实现初始化终结器。最简单的方法是为方法提供最终确定过程。不幸的是,它迫使我们记住总是在最派生的构造函数末尾调用它——这对客户端来说非常糟糕。我认为我可以在基类中使用RAII来达到理想的效果,并编写了这样的代码:

#include <iostream>
using namespace std;
struct Base
{
Base()
{
struct Finishializer
{
~Finishializer()
{
cout << "Base::~Finishializer" << endl;
}
} finishializer;
cout << "Base::Base()" << endl;
}
~Base()
{
cout << "Base::~Base()" << endl;
}
};
struct Derived : Base
{
Derived()
{
cout << "Derived::Derived()" << endl;
}
~Derived()
{
cout << "Derived::~Derived()" << endl;
}
};
int main()
{
Derived();
}

然后我意识到构造函数的调用堆栈求值顺序相反。我的"Finishializer模式"要求首先调用基构造函数,然后在基构造函数的末尾调用派生构造函数(作为下一个堆栈帧)。

不幸的是,C++(在我的例子中是VS2015)以另一种方式工作:它调用派生构造函数,但作为第一条指令,它调用基构造函数(作为下一个堆栈帧)。

它导致以下输出:

Base::Base()
Base::~Finishializer
Derived::Derived()
Derived::~Derived()
Base::~Base()

而不是我梦想中的

Base::Base()
Derived::Derived()
Base::~Finishializer
Derived::~Derived()
Base::~Base()

是否有充分的理由按原样评估堆栈,或者可以按照我的建议对其进行更改以实现"finishializers"?你知道用当前的C++实现来做这些事情的一些替代模式吗?

我还没有找到一种方法来使用基类中的某些东西来完成您想要做的事情。我发现唯一有效的方法是在最派生类的构造函数中添加代码。在您的情况下,

Derived::Derived()
{
cout << "Derived::Derived()" << endl;
Finishializer();
}

一旦您决定从Derived派生并希望在新派生的类中练习Finishializer();的代码,这将是一个令人头疼的维护问题。再一次,我还没有遇到一种设计/实现模式,如果不在最派生的类中显式地添加代码,它就可以工作。

下面的第一种方法,使用工厂函数,有点脏,但我认为它可以满足您的要求。

另一种更愚蠢的方法是要求对具体的、可用的类进行特殊的最终类派生。例如,在Microsoft的ATL库中使用了这种方法,但没有强制执行。但让我们先做工厂:

#include <iostream>
#include <memory>       // std::unique_ptr    
#include <type_traits>  // std::is_base_of
#include <utility>      // std::(forward)
using namespace std;
void say( char const* const s ) { cout << s << "n"; }
#define STATIC_ASSERT( e ) static_assert( e, "`" #e "`  <-- must hold." )
template< class Base, class Derived >
using Is_base_and_derived = is_base_of<Base, Derived>;      // (sic!).
class Base
{
friend class Base_factory;
private:
void finalize_construction() { say( "Finalizing construction." ); }
protected:
virtual ~Base() { say( "Base::<destroy>" ); }
Base() { say( "Base::<init>" ); }
};
class Derived:
public Base
{
friend class Base_factory;
protected:
~Derived() override { say( "Derived::<destroy>" ); }
Derived() { say( "Derived::<init>" ); }
};
class Base_factory
{
private:
static void destroy( Base const* const p )
{ delete p; }
public:
template< class Class >
using Ptr_ = unique_ptr<Class, void(*)(Base const*)>;
template< class Class, class... Args >
static auto make_a( Args&&... args )
-> Ptr_<Class>
{
STATIC_ASSERT(( Is_base_and_derived<Base, Class>::value ));
auto result = Ptr_<Class>{
new Class{ forward<Args>( args )... }, &destroy
};
result->finalize_construction();
return result;
}
};
auto main()
-> int
{
(void) Base_factory::make_a<Derived>();
}

强制派生类方法避免了上面代码中的动态分配,并且避免了对客户端代码一致性的依赖:

#include <assert.h>     // assert    
#include <iostream>
#include <memory>       // std::unique_ptr    
#include <type_traits>  // std::is_base_of
#include <utility>      // std::(forward)
using namespace std;
void say( char const* const s ) { cout << s << "n"; }
#define STATIC_ASSERT( e ) static_assert( e, "`" #e "`  <-- must hold." )

//------------------------------------------- Machinery:
class With_enforced_most_derived;
template< class Some_class, class The_virtual_top = With_enforced_most_derived>
class Construction_finalizer_ final
: public Some_class
, public virtual The_virtual_top
{
public:
template< class... Args >
Construction_finalizer_( Args&&... args )
: The_virtual_top{ typename The_virtual_top::Key{} }
, Some_class{ forward<Args>( args )... }
{ say( "Finalizing construction." ); }
};
class With_enforced_most_derived
{
template< class, class > friend class Construction_finalizer_;
private:
enum Key{};
public:
With_enforced_most_derived()    // Needed for intermediate classes.
{ assert( "Most derived class must be `Construction_finalizer_`" && 0 ); }
With_enforced_most_derived( Key )
{}
};
//------------------------------------------- Example usage:
class Base
: public virtual With_enforced_most_derived
{
public:
virtual ~Base() { say( "Base::<destroy>" ); }
Base() { say( "Base::<init>" ); }
};
class Derived:
public Base
{
public:
~Derived() override { say( "Derived::<destroy>" ); }
Derived() { say( "Derived::<init>" ); }
};

auto main()
-> int
{
#ifdef CRASH_BABY_CRASH
(void) Derived{};
#endif
(void) Construction_finalizer_<Derived>{};
}