5

我有一些基本的继承树:

class Base {
    virtual double func() = 0;
    // functionality is not important for the problem
    // but it's good to know that Base has some virtual functions
};
class DerivedA : public Base {
    virtual double func() {}; // implementation A
};
class DerivedB : public Base {
    virtual double func() {}; // implementation B
};

我有一个容器,其中包含一个DerivedA或多个DerivedB实例的指针。

void f1(std::vector<Base*> a) () { /* some code */ }

int main(in, char**) {
  std::vector<Base*> base_container;
  f1(base_container); // works fine
}

几乎所有东西都可以在 Base 上运行,但是我有一些功能,没有在DerivedAor中指定DerivedB,它是特定于实现的,但可以在DerivedAorDerivedB容器上运行。让我们假设以下代码片段:

void f2(std::vector<DerivedA*> a) () { /* implementation A specific code */ }
void f2(std::vector<DerivedB*> a) () { /* implementation B specific code */ }

调用正确函数的最佳做法是什么?我列出了一些我想到的可能的解决方案,但它们都有很大的缺点。

  1. 我可以将所有数据保存在特定于实现的容器中。但是,由于不是(有充分理由!)的孩子,f1()因此不再起作用。std::vector<DerivedA*>std::vector<Base*>

  2. 我可以一直手动投射所有对象,但这很难看。

  3. f2_a我可以通过给它们提供独特的名称(如and )手动调用需要 Base 容器的正确函数f2_b,但这看起来很难看。

  4. 我可以将 f2 实现为函数模板专业化,但这似乎是滥用。

  5. 我可以尝试使容器上的函数只依赖于容器中对象的函数,然后为它们实现重载函数。这在某些情况下是一个很好的解决方案,但在我的情况下,它确实是容器上的一个函数,在容器上为DerivedAand提供了不同的实现DerivedB,而不仅仅是迭代容器的内容。

你将如何实施这样的事情?如何获得能够同时传递给f1()和的 Container f2()

4

6 回答 6

1

最好的解决方案是让您的继承表示替换,并让基类有一个完整的接口来完成所有工作,使用虚拟方法来选择要使用的派生类方法。然后f2只需要一个基指针向量。

否则,如果派生类中确实有特定的不同接口需要访问(如果有,请花点时间思考一下你的设计做了什么),你最好的方法是不同命名的方法,每个派生类都有一个类型,每个都接受一个基指针向量。使用适当命名的方法,用户可以清楚地知道预期的类型。

于 2013-06-11T15:23:01.217 回答
1

这没有意义。

当源容器可以包含任一类型时,您不能在派生类型的向量上调用函数。

您要么需要将它们分成两个向量,要么使用动态转换,要么通过检查某种type()虚函数。只有这样,您才能放心地调用这些函数。

理想和最干净的选项是您的选项 5,它是动态运行时多态应该工作的方式。

于 2013-06-11T15:12:25.500 回答
1

另一种可能性是做访客。基于派生类型的功能将在访问者的重载 visit() 方法中,而不是在整个容器上工作的重载函数。这也适用于包含两个派生类的容器。

缺点是增加了一些复杂性和依赖性。

于 2013-06-11T15:06:37.470 回答
1
  1. 我不会使用原始指针。shared_ptrptr_...<>来自 Boost 的容器,仅举几个替代品。
  2. 我不会公开使用的容器类型,或者对象存储在容器中的事实。将容器行走与对单个对象的操作分开。
  3. 我会用虚函数实现基于对象类型的选择函数模式,因为这就是虚函数的用途。

现在从理想世界回到我们不幸的星球。

如果您std::vector<Derived*>手头有,您只需std::vector<Derived*>为您的选择调用正确的静态解析函数,重载或明确命名。

如果您所拥有的只是std::vector<Base*>并且您猜测存储在其中的所有指针实际上都指向,您可能想以某种Derived1*方式验证您的猜测(如果实际上您混合了在你不看的时候被你的同事?)你是怎么做到的?一旦你用于检查,你也可以使用其余的一切。Derived1*Derived2*Derived3*dynamic_cast<>dynamic_cast<>

如果您所拥有的只是std::vector<Base*>并且您先验地知道存储在其中的所有指针实际上都指向Derived1*,那么它们可能std::vector<Base*>无缘无故地存储在其中(因为丢失了非常需要的类型信息并且必须以丑陋的方式重新创建)和您应该考虑将其更改为std::vector<Derived1*>.

于 2013-06-11T15:44:14.703 回答
0

模板(或模板和重载的混合)提供了一种可能的解决方案,尽管它们在这里可能会变得笨拙。

template <class T>
void f1(std::vector<T*> a)
{
    // code that doesn't care whether T is Base or DerivedA or DerivedB
}

void f2(std::vector<DerivedA*> a)
{
    // code that requires T = DerivedA
}
void f2(std::vector<DerivedB*> a)
{
    // code that requires T = DerivedB
}

尝试传递 a std::vector<Base*>tof2()会导致编译错误。

于 2013-06-11T15:32:53.487 回答
0

你应该使用 dynamic_cast ,它返回 null 转换错误并使用

if(dynamic_cast<DerivedA>(ptr)!=null) {...}
else if(dynamic_cast<DerivedB>(ptr)!=null) {...}
于 2013-06-11T15:08:53.533 回答