带有继承的c++向量

Posted

技术标签:

【中文标题】带有继承的c++向量【英文标题】:c++ vector with inheritance 【发布时间】:2012-04-14 15:53:36 【问题描述】:

参考代码:

#include <vector>
#include <iostream>

class Func 
public:
    virtual void call() 
        std::cout<< "Func -> call()" << std::endl;
    
;

class Foo : public Func 
public:
    void call() 
        std::cout<< "Foo -> call()" << std::endl;
    
;

class Bar : public Func 
public:
    void call() 
        std::cout<< "Bar -> call()" << std::endl;
    
;

int main(int argc, char** argv) 
    std::vector<Func> functors;

    functors.push_back( Func() );
    functors.push_back( Foo() );
    functors.push_back( Bar() );

    std::vector<Func>::iterator iter;
    for (iter = functors.begin(); iter != functors.end(); ++iter)
        (*iter).call();

当运行该代码时,它会在我的计算机上产生以下输出:

$ ./test
Func -> call()
Func -> call()
Func -> call()

有没有办法确保在这种情况下调用正确的虚函数?我是 C++ 新手,但我最好的猜测是:

(*iter).call();

它被转换为 Func 对象。这是正确的吗?

【问题讨论】:

Store two classes with the same base class in a std::vector的可能重复 是的,但是在我创建这个帖子之前我找不到那个帖子,对不起,我的搜索功能一定不如你的先生 您的情况发生了对象切片,因此派生部分被切掉了。您应该将基于类的指针用于异构对象存储。 【参考方案1】:

您应该使用 shared_ptr 或 unique_ptr 来保存多态类型集合的元素。

现在编写代码时,Foo 和 Bar 实例被强制(复制构造)到 Func 类型的实例中以适应向量。 (原因是为了性能,vector 通过固定大小的值立即存储其元素,但是多态子类的大小在编译时未知,因此它只能存储基类。)

这样更好:

int main(int argc, char** argv) 
    vector<shared_ptr<Func>> functors;

    functors.push_back( make_shared<Func>() );
    functors.push_back( make_shared<Foo>() );
    functors.push_back( make_shared<Bar>() );

    for (auto functor : functors)
        functor->call();

在上面,引用计数指针用于在向量中隐式共享 Func 的异构子类。 (这种间接允许通过地址间接存储任意大小的 Func 子类。)

另外,您可能想看看 std::function 和 std::bind,而不是滚动您自己的仿函数类型。

另一件值得关注的事情是完美的转发和可变模板。

更新:对于旧编译器:

int main(int argc, char** argv) 
    vector<std::tr1::shared_ptr<Func> > functors;

    functors.push_back( std::tr1::make_shared<Func>() );
    functors.push_back( std::tr1::make_shared<Foo>() );
    functors.push_back( std::tr1::make_shared<Bar>() );

    for (size_t i = 0; i < functors.size(); ++i)
        functors[i]->call();

【讨论】:

我似乎无法编译这段代码,这是 boost 代码还是 std c++?我的编译器:i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00) Apple clang version 3.1 (tags/Apple/clang-318.0.58) (based on LLVM 3.1svn) @RonElliott:抱歉,上面的代码使用的是当前的 C++ 标准 (2011)。我会用旧标准写一个版本,等等。 @RonElliott:好的更新使用 C++03+TR1,你应该可以在 g++-4.2 中使用它。如果它仍然不起作用,请告诉我。【参考方案2】:

向量仅按值保存Func 类型,这意味着您的所有时间FooBar 都被切片并转换为基本Func 类型

您需要更改为 std::vector&lt; Func* &gt; 之类的内容并动态分配派生类实例,以便多态调度工作

如果您完全确定在此函数返回后不会将此向量传递给其他函数,作为优化,您可能希望在堆栈中分配实例:

std::vector< Func* > v;
Bar b;
Foo f;
v.push_back( &b);
v.push_back( &f);

【讨论】:

这是有效的,是的,我可以确定这个向量不会被传递,所以这就是我计划实现的方式【参考方案3】:

您的 std::vector 正在存储 Func 对象 - 这意味着当您调用

functors.push_back( Foo() );
functors.push_back( Bar() );

您正在创建 FooBar 对象,然后在将这些对象复制到 Func 对象时“切片”它们。

如果您想多态地使用 Foo 和 Bar,那么更典型的模式是存储某种指针类型的向量(最好 不是“原始”指针),例如

std::vector&lt; std::unique_ptr&lt;Func&gt; &gt;

std::vector&lt; std::shared_ptr&lt;Func&gt; &gt;

或者,如果你真的必须......(但前提是你使用的是没有 shared_ptr 或 unique_ptr 的旧编译器)

std::vector&lt; Func* &gt;

【讨论】:

【参考方案4】:

在 C++ 中,多态性仅适用于指针和引用,而向量直接存储对象的实例。当您调用push_back 时,会调用Func 的复制构造函数,它会构建存储在向量中的Func 对象。

这称为对象切片,您可以通过 *** 中的快速搜索了解更多信息。

解决方案是将指针(或者,更好的是,智能指针)存储到您的对象,这些指针应该分配到其他地方(可能在堆上)。

【讨论】:

【参考方案5】:

一般来说,子类的实例可能比它们的超类的实例大,因此您不应该期望子类适合您的向量槽。

push_back 可能会在内部调用一个复制构造函数(属于Func 类,因为你有vector&lt;Func&gt;)所以内部向量槽确实是Func 而不是其他一些类。

【讨论】:

【参考方案6】:

你的问题是你有一个Func的向量,但是方法只能通过引用或指针多态地调用。

【讨论】:

【参考方案7】:

有一种非常简单的方法可以在不使用指针的情况下创建向量,方法是创建将继承 A 类和 B 类的子类

#include <iostream>
#include <vector>

using namespace std;

struct BASE 
    string name = "Base class ";
;

struct A : BASE 
     string a = " class a ";
;

struct B : BASE 
     string b = " class b ";
;

struct C : A, B
    //using base name from A or B// yeah is hardcoded
    string name = A::name;
;

int main()


    C c;
    c.a = "s";
    c.b = "h";
    vector<C> vec;

    vec.push_back(c);
    c.b = "j";
    vec.push_back(c);

    cout << vec[0].a << endl;
    cout << vec[0].b << endl;
    cout << vec[0].name << endl;

    cout << vec[1].a << endl;
    cout << vec[1].b << endl;
    cout << vec[1].name << endl;

    return 0;

【讨论】:

以上是关于带有继承的c++向量的主要内容,如果未能解决你的问题,请参考以下文章

带有浮点索引的 C++ 数组/向量

C++ 不允许带有负索引的向量?

带有列的 C++ 文本文件到 2D 向量中

带有结构的 C++ 向量不起作用?

您可以在 C++ 中动态分配带有向量作为字段的类吗?

带有向量和字符串的 C++ 分段错误