在同一个向量中存储两个不同的类(具有相同的继承基类)? (没有提升)
Posted
技术标签:
【中文标题】在同一个向量中存储两个不同的类(具有相同的继承基类)? (没有提升)【英文标题】:store two different classes (with same inherited base class) in same vector? (no boost) 【发布时间】:2011-12-21 07:27:41 【问题描述】:我有两个不同的类(First、Second),它们继承了相同的基类(Base)。我想将 First 和 Second 的实例存储在同一个向量中,而不会将它们的类拼接到 Base 类中。如果我使用vector,就会发生这种拼接,如下所示:
#include <iostream>
#include <vector>
class Base
public:
Base()
virtual void test() std::cout << "I am just the base class\n";
;
class First : public Base
public:
First()
void test() std::cout << "This is the First class\n";
;
class Second : public Base
public:
Second()
void test() std::cout << "This is the Second class\n";
;
int main()
First *f = new First();
Second *s = new Second();
// First, I keep a vector of pointers to their base class
std::vector<Base> objs;
objs.push_back(*f);
objs.push_back(*s);
objs[0].test(); // outputs "I am just the base class"
objs[1].test(); // outputs "I am just the base class"
最终,当两个对象放入vector时,它们就被拼接了。有没有办法(没有提升)将这两个对象复制到同一个向量中?向量不是我要找的,我想复制对象。
这里有很多很好的答案。不幸的是,我不能假设 C++11,所以我最终利用了克隆构造函数,并提示存储指针。但最终,克隆允许我先创建副本。
这是我的最终解决方案:
#include <iostream>
#include <vector>
class Base
public:
Base()
virtual void test() std::cout << "I am just the base class\n";
virtual Base* clone() const = 0;
;
class First : public Base
public:
First()
void test() std::cout << "This is the First class\n";
First* clone() const return new First(*this);
;
class Second : public Base
public:
Second()
void test() std::cout << "This is the Second class\n";
Second* clone() const return new Second(*this);
;
int main()
First *f = new First();
Second *s = new Second();
std::vector<Base *> bak;
bak.push_back(f->clone());
bak[0]->test();
【问题讨论】:
我认为您的意思是“切片”。 “拼接”正好相反。 请确保在使用不带智能指针的 clone() 时,删除您克隆的所有内容 【参考方案1】:要复制继承的对象而不进行切片,需要在基类中定义一个 clone() 函数,并在派生类中重写:
class Base
public:
Base* clone() const = 0;
class Derived
Derived* clone() const return new Derived(*this);
这样,即使有一个指向基类的指针,你也会得到一个派生类的副本。
【讨论】:
【参考方案2】:是的,您创建了一个指向 Base
的指针的 vector
:
std::vector<Base*> objs;
或者,更好的是,vector
指向基类的智能指针,因此您无需担心内存管理。
【讨论】:
我正在寻找复制对象,抱歉没有澄清。 @gnychis 我不明白。如果要复制对象,请使用复制构造函数... @gnychis:然后深度复制指针。否则无法完成【参考方案3】:我不知道你为什么要复制对象,但如果是为了方便内存管理,那么这样做:
std::vector<std::shared_ptr<Base> > objs;
objs.push_back(std::make_shared<First>());
objs.push_back(std::make_shared<Second>());
【讨论】:
注意:std::make_shared 是 c++11,在 c++03 中不可用 @mark:std::shared_ptr
在 C++03 中也不可用。关键是OP可以用智能指针解决他的问题。智能指针的命名空间无关紧要。他也可以使用boost::
。
@sad_man:OP 将其帖子命名为“(无提升)”。不,C++03 中没有 std::shared_ptr (除了一些实验性扩展:shared_ptr 在 C++11 中已被批准为“标准”,主要是通过使用 C++11 特定功能重写 boost::shared_ptr) 【参考方案4】:
按照您提出问题的方式,这是不可能的:
vetor<Base>
包含 Base
s 序列,而 First
和 Second
都是 Base
以及更多内容。
就其本质而言,vector 包含一系列相同的对象(所有 Base)和 push_back,实际上并没有将对象放在 vector 中,而是将对象复制到在 vector 末尾构造的那个中(并且是一个Base
对象,因此只复制了Base
子组件。
要解决这个问题,你必须让你的向量不是“包含”Base
-s,而是“引用”Base-s。这需要指针
std::vector<Base*> v;
v.push_back(new First);
v.push_back(new Second);
但这也需要内存管理。这可以通过两种方式完成:
1) 使用一些“拥有”基础的智能指针。
在 C++11 中有 std::shared_ptr
和 std::unique_ptr
。
在 C++03 中,您必须为自己安排一个引用计数指针,例如
template<class T>
class ptr
public:
ptr() :p(), c()
explicit ptr(T* z) :p(z), c(new unsigned(1))
ptr(const ptr& s) :p(s.p), c(s.c) if(c) ++*c;
ptr& operator=(const ptr& s)
if(this!=&s)
if(c && !--*c) delete p; delete c;
p = s.p; c=s.c;
if(c) ++*c;
return *this;
~ptr() if(c && !--*c) delete p; delete c;
T* operator->() const return p;
T& operator*() const return *p;
private:
T* p;
unsigned* c;
;
并将你的向量声明为
std::vector<ptr<Base> > v;
v.push_back(ptr(new First));
v.push_back(ptr(new Second));
v[0]->test();
v[1]->test();
销毁v
,会销毁ptr,而ptr又会销毁对象。
注意: Base 还必须声明一个虚拟析构函数,否则派生对象不能被多态销毁。
2) 或者,您可以“自定义”向量以“拥有”指针本身。 这可以通过类似的方式获得
template<class T>
class owningvector: public std::vector<T*>
public:
~owningvector()
for(size_t i=0; i<size(); ++i)
delete at(i);
owningvector()
private:
//disable vector copy and assign
owningvector(const owningvector&);
owningvector& operator=(const owningvector&);
;
owningvector<Base> ov;
ov.push_back(new First);
ov.push_back(new Second);
注意: Base
仍然需要虚拟析构函数。
另外,std::vector
和 ownvector
在销毁时不是多态的,不要将自己用作多态类型。
3) 如果你在 on_stack 上做所有事情,你可以很容易地做到
int main()
First first;
Second second;
std::vector<Base*> v;
v.push_back(&first);
v.push_back(&second);
v[0]->test();
v[1]->test();
//no other memory management is required:
// v is destroyed without doing anything special.
// second and first are destroyed just further.
【讨论】:
【参考方案5】:重新定义复制构造器,然后使用:
std::vector<Base*> objs;
objs.push_back(new First(*f));
objs.push_back(new Second(*s));
【讨论】:
【参考方案6】:你说你想复制你的对象,我很感动。 通常,多态性和复制/赋值不能很好地协同工作, 但是您可以使用信封成语来做到这一点。类似的东西:
class Base
Base* myImpl;
virtual Base* clone() const
// or perhaps just abort, since this one shouldn't
// ever be called in practice.
return new Base( myImpl->clone() );
protected:
Base() : myImpl( NULL )
Base( Base* impl ) : myImpl( impl )
public:
Base( Base const& other ) : myImpl( other.myImpl->clone() )
virtual ~Base()
virtual void test() myImpl->test();
;
class First : public Base
virtual Base* clone() const
return new First( *this );
First()
public:
static Base createInstance() return Base( new First );
virtual void test() std::cout << "In First" << std::endl;
;
class Second : public Base
virtual Base* clone() const
return new Second( *this );
Second()
public:
static Base createInstance() return Base( new Second );
virtual void test() std::cout << "In Second" << std::endl;
;
这会产生一个类Base
,它的行为是多态的,但是
支持复制和分配(并且分配可能会改变有效
类型),并且不会切片。它还会导致严重的性能损失,
并且通常不是想要的——根据我的经验,大多数
多态类有身份,不应该是可复制的或
可分配的。但这是一个可能的解决方案,并且在某些情况下可能有用
具体案例。
【讨论】:
以上是关于在同一个向量中存储两个不同的类(具有相同的继承基类)? (没有提升)的主要内容,如果未能解决你的问题,请参考以下文章