我有两个不同的类(First,Second)继承了相同的基类(Base)。我想在同一个向量中存储First和Second的实例,而不将它们的类拼接到Base类。如果我使用向量,将发生这种拼接,如下所示:
#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"
}
最终,当两个物体被放入矢量时,它们会被拼接。有没有办法(没有提升)将这两个对象复制到同一个向量中?矢量不是我想要的,我想复制对象。
这里有很多好的答案。不幸的是,我不能假设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();
}
答案 0 :(得分:3)
要复制继承的对象而不进行切片,您需要在基类中定义clone()函数并在派生类中重写:
class Base
{
public:
Base* clone() const = 0;
}
class Derived
{
Derived* clone() const { return new Derived(*this); }
}
这样,即使使用指向基类的指针,您也将获得派生类的副本。
答案 1 :(得分:2)
是的,您创建了vector
指向Base
的指针:
std::vector<Base*> objs;
或者更好的是,基类的智能指针vector
,所以你不必担心内存管理。
答案 2 :(得分:2)
我不知道你为什么要复制这些对象,但如果是为了便于内存管理,那么这样做:
std::vector<std::shared_ptr<Base> > objs;
objs.push_back(std::make_shared<First>());
objs.push_back(std::make_shared<Second>());
答案 3 :(得分:2)
顺便提一下你的问题,这是不可能的:
vetor<Base>
包含Base
个序列,而First
和Second
都是Base
加上更多内容。
就其本质而言,vector包含相同对象的序列(所有Base)和push_back,实际上并不将对象放在向量中,而是将对象复制到在向量末尾构造的对象(和是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
,将破坏将反过来破坏对象的ptrs。
注意: Base还必须声明虚析构函数,否则派生对象不能被plymorphically销毁。
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.
}
答案 4 :(得分:1)
定义复制构造函数,然后使用:
std::vector<Base*> objs;
objs.push_back(new First(*f));
objs.push_back(new Second(*s));
答案 5 :(得分:1)
我说你想要复制你的对象,我感到很沮丧。 通常,多态性和复制/分配不能很好地协同工作, 但你可以用letter-envelop习语来做到这一点。类似的东西:
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
的行为多态,但是
支持复制和分配(并且分配可能会改变有效
类型),并不会切片。它还会导致严重的性能损失,
并且通常不是我想要的 - 在我的经验中,大多数
多态类具有标识,不应该是可复制的
转让。但它是一种可能的解决方案,并且在某些方面可能很有用
具体案例。