2

假设我有一个从基类派生的类。多态性允许我通过指向基类的指针(使用虚函数)调用派生类的函数。换句话说,我可以让派生类的成员“假装”成为基类的成员。牢记这一点,基类是否有可能在其构造函数中实际构造派生类的成员(然后“假装”为基类的成员,因此没有任何问题)?从逻辑上讲,我认为这不应该起作用,但我无法弄清楚如何在语法上做到这一点。如果这是不可能的,为什么不呢?

我知道我可以有一个单独的函数来构造派生类的成员并将其作为基类的成员返回。如果这最终是不可能的,这就是我将要做的,但是将它作为构造函数会更干净(因为这就是这个单独的函数基本上是什么)。

编辑:这是我正在寻找的一个例子:

class base
{
  base()
  {
    this=new derived(); //This is what I am looking for
  }

  virtual func();
};

class derived : public base
{
  derived() : base()
    {}

  func()
  {
    ...
  }
};

如上所述,这可以通过以下方式实现:

base *fake_base_constructor()
{
  return new derived();
}

实际上,会有多个派生类,基类构造函数会根据参数在它们之间进行选择,但从概念上讲,您只需要一个。

4

3 回答 3

4

听起来您想要奇怪地重复出现的模板模式。它允许基类通过给它一个模板参数来知道从它派生的类型,该模板参数是派生类型:

template <class D>
class Base
{ };

class Derived : public Base<Derived>
{ };

现在您可以DerivedBase. 例如,构造函数Base可以做:

template <class D>
void Base<D>::some_base_member()
{
  D d;
};

事实上,这种模式的一个重要结果是您可以DerivedBase类中调用成员。例如:

template <class D>
void Base<D>::some_base_member()
{
  static_cast<D*>(this)->some_derived_member();
};
于 2012-12-22T00:26:54.453 回答
2

听起来您想通过将某些内容传递给构造函数来选择获得的事物类型?您的示例提供了静态选择,但文本暗示将有多种选择。我很好奇你打算如何使用它,特别是。

假设你想做这样的事情:

enum Types
{
    Type_A,
    Type_B,
};

class Base
{
public:
    Base(Types t)
    {
        switch (t)
        {
        case Type_A:
            this = <magic> A;
            break;

        case Type_B:
            this = <magic> B;
            break;
        }
    }
};

但问题是这不是 C++ 的工作方式;您的对象空间在构造函数被命中时已经分配,​​因此您不能只是将派生项填充到该空间中。例如,假设您将其作为类成员变量:

struct SomeStruct
{
    int i;
    Base b;
    float f;

    SomeStruct();
}

SomeStruct::SomeStruct() : i(4), b(Type_A), f(3.14f)
{
}

这根本行不通。听起来最直接的解决方案是使用工厂功能;它明确表明您必须处理指针,这是程序员习惯看到的东西:

Base* BaseFactory(Types t)
{
    switch (t)
    {
    case Type_A:
        return new A;

    case Type_B:
        return new B;
    }
}

你可以用奇怪的重复模板模式做类似的事情,但它不像另一个答案中建议的那么简单(部分原因是你可能想要运行时多态性,这在 Wikipedia 文章中没有提到),但这并不是真的必要;无论您的解决方案如何,构建这些东西的代码模块都必须了解可以创建的所有内容,并考虑到一个约束,最好在基类之外的某个地方拥有这些知识。只是我的意见,但我认为从长远来看,避免构建大束字符串是最安全的。

于 2012-12-22T02:35:38.427 回答
0

你问:

“基类是否有可能在其构造函数中实际构造派生类的成员(然后“假装”为基类的成员,因此没有任何问题)?”

是的,有很多方法可以在基类中进行派生类特定的初始化。

这是我写的一篇博客文章中的一个例子,题为“如何使用零件工厂避免后期施工”

#include <stdio.h>

namespace apiLevel {
    enum Handle {};

    Handle newButton( char const title[] )
    {
        printf( "apiLevel::newButton(\"%s\")\n", title );
        return Handle();
    }

    Handle newListbox( char const [] = "" )  { return Handle(); }
    Handle newStatic( char const [] = "" )   { return Handle(); }
}  // namespace apiLevel

class Widget
{
private:
    apiLevel::Handle    handle_;

protected:
    struct ApiWidgetFactory
    {
        virtual apiLevel::Handle newWidget( char const title[] ) const
        {
            return apiLevel::newStatic( title );  // Reasonable.
        }
    };

public:
    explicit Widget(
        char const                  title[] = "\"? (unspecified)\"",
        ApiWidgetFactory const&     factory = ApiWidgetFactory()
        )
        : handle_( factory.newWidget( title ) )
    {}
};

class Button
    : public Widget
{
protected:
    struct ApiWidgetFactory: Widget::ApiWidgetFactory
    {
        virtual apiLevel::Handle newWidget( char const title[] ) const
        {
            return apiLevel::newButton( title );    // Derived class specific.
        }
    };

public:
    explicit Button(
        char const                  title[],
        ApiWidgetFactory const&     factory = ApiWidgetFactory()
        )
        : Widget( title, factory )
    {}
};

int main()
{
    Button  button( "Just a button" );
}

上述方法的主要优点是它为客户端代码提供了一个类型安全的接口,具有普通的面向 RAII 的构造(即,充分利用类不变量)。

主要缺点是它需要维护两个并行的类层次结构,尽管这主要是一个将代码放在哪里的问题。

有关更多信息,请参阅该博客文章,该文章还进一步链接到常见问题解答,其中还提供了一些其他方法。博客帖子有点直接来自马的嘴,即我的。常见问题解答项目是我说服马歇尔为常见问题解答编写该问题和解决方案的结果。

于 2012-12-22T01:32:07.740 回答