带有继承的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
类型,这意味着您的所有时间Foo
和Bar
都被切片并转换为基本Func
类型
您需要更改为 std::vector< Func* >
之类的内容并动态分配派生类实例,以便多态调度工作
如果您完全确定在此函数返回后不会将此向量传递给其他函数,作为优化,您可能希望在堆栈中分配实例:
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() );
您正在创建 Foo
和 Bar
对象,然后在将这些对象复制到 Func
对象时“切片”它们。
如果您想多态地使用 Foo 和 Bar,那么更典型的模式是存储某种指针类型的向量(最好 不是“原始”指针),例如
std::vector< std::unique_ptr<Func> >
std::vector< std::shared_ptr<Func> >
或者,如果你真的必须......(但前提是你使用的是没有 shared_ptr 或 unique_ptr 的旧编译器)
std::vector< Func* >
【讨论】:
【参考方案4】:在 C++ 中,多态性仅适用于指针和引用,而向量直接存储对象的实例。当您调用push_back
时,会调用Func
的复制构造函数,它会构建存储在向量中的Func
对象。
这称为对象切片,您可以通过 *** 中的快速搜索了解更多信息。
解决方案是将指针(或者,更好的是,智能指针)存储到您的对象,这些指针应该分配到其他地方(可能在堆上)。
【讨论】:
【参考方案5】:一般来说,子类的实例可能比它们的超类的实例大,因此您不应该期望子类适合您的向量槽。
而push_back
可能会在内部调用一个复制构造函数(属于Func
类,因为你有vector<Func>
)所以内部向量槽确实是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++向量的主要内容,如果未能解决你的问题,请参考以下文章