C++模板和继承

6

我的C++框架有一个按钮(Button)类,它派生自控件(Control)类。因此,接受Control的函数可以将Button作为其参数。目前为止还好。

我还有一个List<T>(列表)类。然而,List<Button>并没有从List<Control>派生出来,这意味着接受控件列表的函数无法将按钮列表作为其参数。这很不幸。

也许这是个愚蠢的问题,但我不知道怎么解决:( List<Button>应该从List<Control>派生出来,但我不知道如何"自动"完成这个过程。


好的,我的错。我有指针列表,而不是对象列表。当然,将我的Button *存储在Control *列表中会修复函数调用问题,我通常这样做,除非我想要一个Button *列表以将它们用作Button,而不是Control。 - ggambetta
现在,你有什么?是一个List<Button*>和List<Control*>,还是只有一个List<Control*>?我有点困惑,请说明一下 :) - Johannes Schaub - litb
如果你只有一个 List<Control*>,并且想要在个别元素是按钮类型时进行特殊处理,那么你将不得不使用 dynamic_cast。 - Johannes Schaub - litb
你的列表是什么?希望你不要重新发明像std::vector这样完美的数据结构。 - jalf
我有这个void doSomething(List<Control*> lControls);void foo (void) { List<Button*> lButtons; .... doSomething(lButtons); // 我想这样做,但我不能 } - ggambetta
5个回答

7

Stroustrup在他的FAQ中有一个条目:

为什么不能将vector<Apple*>赋值给vector<Fruit*>

你可以用两种方法解决它:

  • 使List包含指向Control的指针。然后接受List<Control*>
  • 将您的函数设置为模板。然后,您仍然可以使用List<Button>List<Control>,但这需要更多的样板代码,并且大多数情况下不是必要的。

这里是第二个选择的代码。第一个选择已经由其他答案解释过了:

class MyWindow {
    template<typename T>
    void doSomething(List<T> & l) {
        // do something with the list...
        if(boost::is_same<Control, T>::value) {
            // special casing Control

        } else if(boost::is_same<Button, T>::value) {
            // special casing Button

        }

    }
};

为了仅限于List<派生自Control>,需要添加更多代码来限制doSomething的使用(如果您想了解,可以查找enable_if)。

请注意,这种代码(查看类型)应该尽量避免。您应该使用虚函数来处理此类事情。在Control中添加一个函数doSomething,并在Button中覆盖它。


6
如何使用指针?只需创建一个list<Control*>列表,并将您喜欢的任何继承自Control的对象放入其中。

如果您决定使用指针列表,您将需要记得在使用完后删除对象(您将失去自动内存管理)。要恢复它,请查看Boost的ptr_container库。它有一个容器,将指针列表管理得就像标准列表一样。 - Tom Leys

6
我很抱歉地告诉你,如果你使用实例列表来控制而不是指向控件的指针,那么你的按钮将无法使用(请搜索“对象切片”)。如果它们是指针列表,那么要么像其他人建议的那样将list<button*>变成list<control*>,要么将list<button*>复制到一个新的list<control*>中,并将其传递到函数中。或将该函数重写为模板。

因此,如果您以前有一个名为doSomething的函数,该函数将一个控件列表作为参数,请将其重写为:

template <class TControl>
void doSomething( const std::list<TControl*>& myControls ) {
  ... whatever the function is currently doing ...
}

void doSomethingElse() {
   std::list<Button*> buttons;
   std::list<Control*> controls;
   doSomething( buttons );
   doSomething( controls );
}

它看起来很丑,但它是标准模板库和现代C++编程的核心。有时间可以查看std::list本身的源代码。不太好看。 - Michel
我对此的主要问题是(如果我理解正确),编译器可以为每种使用的类型生成位对位相同的函数,而这完全是合法的。哦,好吧。 - BCS

2

使用List<Control*>而不是List<Button>,这样指向的是Buttons。这样,您的函数只需要采用一个类型: List<Control*>。


抱歉没有表达清楚。我确实使用指针(智能指针)。该函数需要一个Control类型的列表。问题是我想要传递一个Button类型的列表。 - ggambetta

0
通常,C++编写在列表、序列等上执行算法的方式是将迭代器作为参数提供。
template < class iterator >
doSomething(iterator beg, iterator end);

这解决了List< Button* >不是从List< Control* >派生的问题。(使用模板List< T* >也可以,但这有点使您的函数泛型但又不完全)

根据我的经验,编写操作迭代器的良好模板化函数可能需要很多(太多...)工作,但这是“C++方式”...

如果您选择这条路,请考虑使用Boost.ConceptCheck。它会让您的生活变得轻松得多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接