C++ 如何避免两步初始化

C++ 如何避免两步初始化,c++,inheritance,C++,Inheritance,我希望在我的代码中遵循RAII(ResourceAcquisition is initialization)的习惯用法,但我也在做模板方法模式,在这里我开发类的通用版本,并使用它们为某些事情构建公共代码库。有时我需要强制执行一个初始化序列,在这里我需要调用构造函数中的特定对象的虚函数,但是C++中不可能。我能想到的唯一解决方案是两步初始化,在创建对象后调用init函数,但这打破了RAII习惯用法。有什么解决办法吗 #include <memory> class A { public

我希望在我的代码中遵循RAII(ResourceAcquisition is initialization)的习惯用法,但我也在做模板方法模式,在这里我开发类的通用版本,并使用它们为某些事情构建公共代码库。有时我需要强制执行一个初始化序列,在这里我需要调用构造函数中的特定对象的虚函数,但是C++中不可能。我能想到的唯一解决方案是两步初始化,在创建对象后调用init函数,但这打破了RAII习惯用法。有什么解决办法吗

#include <memory>

class A
{
public:
    A()
    {
        // I want to call B's foo() here
    }
    virtual void foo() = 0;
};

class B : public A
{
public:
    B() {}
    virtual void foo() {}
};

int main()
{
    std::unique_ptr<A> a(static_cast<A*>(new B));

    // Use b polymorphically from here...
}
#包括
甲级
{
公众:
()
{
//我想在这里叫B的foo()
}
虚拟void foo()=0;
};
B类:公共A
{
公众:
B(){}
虚拟void foo(){}
};
int main()
{
标准::独特的ptr a(静态演员阵容(新B));
//从这里多态地使用b。。。
}
编辑:看来我的例子太简单了,下面是另一个可能更好地说明我要做什么的例子:

#include <memory>
#include <iostream>

class A
{
public:
    A()
    {
        foo();
    }
    void init()
    {
        // bar() must be executed every time A is created but the derived classes will decide how to implement it
        bar();
    };
    void foo() { std::cout << "Part 1 of algo\n";  };
    virtual void bar() = 0;
};

class B : public A
{
public:
    B() {}
    virtual void bar() { std::cout << "Part 2 of algo\n"; }
};

int main()
{
    std::unique_ptr<A> a(static_cast<A*>(new B));
    a->init(); // I don't want to have to call this every time I instanciate it
}
#包括
#包括
甲级
{
公众:
()
{
foo();
}
void init()
{
//bar()必须在每次创建时执行,但派生类将决定如何实现它
bar();
};

void foo(){std::cout不要为此使用虚拟分派

A(std::function<void(A*)> foo)
{
    foo(this);
}
A(std::function foo)
{
傅(本);
}

A(std::function foo)
{
foo();
}
现在
A
的子类型将
foo
的主体传递给
A
构造函数。当然,这
foo
最好不要依赖于有效的
B
甚至
A

您可以将构造限制为私有/受保护的路径,并在其最终伪构造函数中要求分阶段初始化。可能有一个CRTP助手,子代应该使用它来实现分阶段(DRY)

但这一要求是一个危险信号,表明你的类型可能充满了通心粉逻辑。有时是必需的,但通常是可以避免的

另一种完全不同的方法是,如果不符合你的需要,扔掉PuxC++ C++内置的OO系统。有很多方法来实现OO;假设内置C++方式对你的问题最有效的是锤钉解决方案(你有一个锤子,所以问题看起来像一个钉子)。 例如,如果您将类型规范化并将继承作为实现细节,或者编写自己的vtables,那么两阶段init的“问题”可能会消失(因为它们将init包装在另一个抽象层中)


学习Smalltalk、Perl、C、Python、C#/COM/.net、MFC、Objective-C、Haskell、Javascript和Lua(作为我脑海中的一组示例)中惯用OO的“工作原理”,应该可以为您提供一种不同的方法来实现“手动”OO解决方案(有一些重叠)。

下面是我的解决方案,使用静态工厂函数来创建B(通过使用模板可能是任何类型的派生类)在构造后调用onNew()。创建B的唯一方法是调用create函数,该函数实例化B并返回一个共享指针,该共享指针具有一个调用“destroy”的自定义删除程序自动运行。这允许进行后期构造和预破坏工作(也可以针对其他类型的指针进行修改)

对于那些仍然对我为什么这么做感到困惑的人来说,这是因为:

  • 我可以通过调用onNew()和onDelete()中的函数(如foo()和bar())在中强制执行初始化和销毁序列

  • 这些函数必须按我希望的顺序调用,但它们必须由派生类定义

  • 这给派生类的创建者留下了很少的出错空间,因为他们有一个非常严格的实现

  • 由于用户不必手动调用onNew()和onDelete(),所有操作都是自动完成的,因此在类的使用过程中也不会出现错误

  • 我可以向上转换到一个and,并以多态方式使用它,从而更加确信它处于有效状态并且已正确初始化,在创建对象后无需进一步检查

  • 在onNew()中,我还可以从foo()返回一个值,并将其设置为成员变量,然后在其他函数中对其进行操作。在这些函数中,我不需要检查这些成员变量是否正确设置,因为它是在后期构造中强制执行的

-

#包括
#包括
#包括
使用名称空间std;
甲级
{
受保护的:
A(){}
虚拟~A(){}
虚拟void onNew()
{
foo();
}
虚拟void onDelete()
{
bar();
}
无效销毁()
{
onDelete();
删除此项;
}
公众:
模板静态共享\u ptr create()
{
static_assert(std::is_base_of::value,“T必须是a的后代”);
共享的(新的t(),[](t*p){p->destroy();});
t->onNew();
返回t;
}
虚拟void foo()=0;
虚拟空心条()=0;
};
B类:公共A
{
朋友A;
受保护的:
B(){}
虚拟~B(){}
公众:
虚拟void foo()重写
{

为什么您需要在
A
的构造函数中调用
B
foo
?为什么
B
的构造函数不能调用它,在这种情况下它会正常工作?这不像您可能认为的那样工作,因为您需要一个B对象来调用B::foo(),并且直到
B::B()您才有一个B对象
开始运行。这只有在
A::A
返回后才会发生。您的示例看起来就像是常规继承。您能否将模板模式用法添加到其中,使其与您的代码实际外观最为相似?如果不获得未定义的行为,您不能从
A
的构造函数中调用任何
B
方法,这是错误的按法律规定根本不可能
A(std::function<void()> foo)
{
    foo();
}
#include <type_traits>
#include <memory>
#include <iostream>

using namespace std;

class A 
{
protected:
    A() {}
    virtual ~A() {}

    virtual void onNew() 
    {
        foo();
    }

    virtual void onDelete() 
    {
        bar();
    }

    void destroy() 
    {
        onDelete();
        delete this;
    }

public:
    template <class T> static shared_ptr<T> create() 
    {
        static_assert(std::is_base_of<A, T>::value, "T must be a descendant of A");
        shared_ptr<T> t(new T(), [](T *p) { p->destroy(); });
        t->onNew();
        return t;
    }

    virtual void foo() = 0;
    virtual void bar() = 0;
};

class B : public A 
{
    friend A;

protected:
    B() {}
    virtual ~B() {}

public:
    virtual void foo() override 
    {
        cout << "B foo";
    }

    virtual void bar() override
    {
        cout << "B bar";
    }
};

int main() 
{
    shared_ptr<A> a = static_cast<shared_ptr<A>>(A::create<B>());
}