【问题标题】:C++ Generic List AssignmentC++ 通用列表赋值
【发布时间】:2011-02-14 04:58:01
【问题描述】:

我显然已经在 J​​ava 领域停留了太久...是否可以执行以下 Java 代码的 C++ 等效项:

interface Foo {}

class Bar implements Foo {}

static List<Foo> getFoo() {
  return new LinkedList<Foo>();
}

static List<Bar> getBar() {
  return new LinkedList<Bar>();
}

List<? extends Foo> stuff = getBar();

其中 Foo 是 Bar 的子类。

所以在 C++ 中......

std::list<Bar> * getBars()
{
  std::list<Bar> * bars = new std::list<Bar>;
  return bars;
}

std::list<Foo> * stuff = getBars();

希望这是有道理的......

【问题讨论】:

  • 你确定这在 Java 中有效吗?因为我刚刚检查过,并不是因为 Java 不支持协变泛型。此外,这只有在 BarFoo 的子类时才有意义。
  • 正确。我的错。我现在已经更正了代码 sn-p。

标签: c++ generics list templates stl


【解决方案1】:

改变

std::list<Bar> & getBars()

std::list<Bar> getBars()

这原则上会按值返回,但是有复制构造省略机制,所以优化编译器应该可以优化

std::list<Bar> bars (getBars());

不涉及复制构造函数,直接构建bars

另外,在 C++0x 中还有移动构造函数,所以即使复制/移动由于任何原因没有被省略,上面的代码也是高效的。

编辑错过的子类问题:

一般来说,这在 C++ 中是不可能做到的(我假设FooBar 的一个超类,否则你所做的没有什么意义)。使用一些reinterpret_cast 黑魔法可能是可能的,但我强烈建议不要这样做。

最接近的近似值可能是这样(getBars() 相应调整):

std::list <Bar*>  bars (getBars ());
std::list <Foo*>  foos (bars.begin (), bars.end ());

但是,这会给您带来一些重要的内存管理负担。使用某种自动指针可能会有所帮助,但据我所知,只有 shared_ptr 可以在容器中使用。

最后,Foo/Bar 类层次结构应该使用虚函数,否则你想要做的几乎肯定不会工作(即除非你真的想忽略子类中的任何覆盖)。

【讨论】:

  • -1 因为您没有回答作者提出的将std::list&lt;Bar&gt; 协变赋值为std::list&lt;Foo&gt; 的问题。
  • @Space_C0wb0y:是的,我错过了这个问题。编辑涵盖了它,尽管它没有提供直接的答案。
  • 删除了反对票。但是我仍然想知道为什么没有人认为这甚至在 Java 中都行不通。
  • @Space_C0wb0y:以这种确切的形式,它在 Java 上不起作用,是的。但是例如List &lt;? extends Foo&gt; foos = new ArrayList &lt;Bar&gt; (); 是 Java 中的有效语句。
  • 如果BarFoo 的子类,则此方法有效。 OP 则相反。
【解决方案2】:

要么我在这里完全搞错了,要么你的 Java 代码不起作用。我刚试过。它不起作用。原因是,Java 中的通用容器are not covariant(这意味着,如果BarFoo 的子类,它也不起作用)。其次,如果FooBar 的子类,那么您可以将Foo 引用分配给Bar,但不能反过来(Java 和C++ 都不是)。

C++ 中的容器也不是协变的。但是你可以做

std::list<Foo*> FooList;
// fill FooList;
std::list<Bar*> BarList(FooList.begin(), FooList.end());

如果FooBar 的子类。然而,这将导致FooList 中的所有指针都复制到BarList 中。这意味着,如果您之后通过添加或删除元素来更改 FooList,这些更改将不会反映在 BarList 中。

【讨论】:

    【解决方案3】:

    在我看来,这在 C++ 中没有意义。

    首先,您返回一个不再存在的引用。为避免这种情况,您可以将 std::list 作为要在函数中修改的参考参数传递,如

    void fillFoos( std::list< Foo > & foos )
    

    其次,foos 不是条形,不能相互复制,除非我认为你提供了正确的复制运算符。

    但是如果你使用继承,你所有的 foos 和 bar 应该是指针(如果你可以将它们智能为来自 boost 或 tr1 的 shared_ptr 指针)。但这并不意味着副本有效。

    我不太确定你想做什么,但是在这种情况下从 JAVA 转换到 C++ 是行不通的。如果您创建 foos,它们将自动从 bar 中获取所有内容。

    std::list< Foo > foos; // just work fine
    

    如果您想要构建为 foos 的条形列表:

    std::list< Bar * > bars;
    bars.push_back( new Foo() );
    

    或者就像我将它放在带有 shared_ptr 的真实 C++ 代码中:

    typedef boost::shared_ptr< Bar >;
    typedef boost::shared_ptr< Foo >;
    typedef std::list< BarPtr > BarList;
    
    BarList bars;
    
    bars.push_back( FooPtr( new Foo() ) );
    

    【讨论】:

      【解决方案4】:

      另外,new std::list 将返回一个指针,而不是一个引用。

      【讨论】:

        【解决方案5】:

        你需要对象来支持这些操作的构造函数,所以你不应该返回一个引用,而是一个将被复制的直接对象,否则返回一个指针,即:

        std::list<Bar> getBars()
        {
            std::list<Bar> Bars;
            return Bars;
        }
        

        std::list<Bar>* getBars()
        {
            return new std::list<Bar>();
        }
        

        std::list 支持复制另一个列表的对象,但前提是该列表的类型相同,即:不能从std::list&lt;Bar&gt; 隐式复制到std::list&lt;Foo&gt;,但可以从std::list&lt;Foo&gt; 复制到std::list&lt;Foo&gt;.

        【讨论】:

        • 请注意,这几乎肯定会导致内存泄漏。
        • 是的,如果你不小心,最好将它包装在一个智能/共享指针中,这样当它不再使用时它会自动删除
        【解决方案6】:

        一切都很好,除了你返回对局部变量的引用。它在堆栈中,将在函数返回后释放。

        【讨论】:

        • 好吧,我迷路了。我刚刚尝试了他的 Java 并且是 C++ 代码,但都不起作用。两种语言都没有协变泛型/模板,那么这应该如何工作?!
        • @Space_C0wb0y - 我刚刚意识到他有 Foo 和 Boo ......这不应该工作,你是对的
        猜你喜欢
        • 1970-01-01
        • 2021-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-06
        • 2019-05-13
        相关资源
        最近更新 更多