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