我处于一个相当具体的情况,要求我使用原始指针向量作为类成员:
vector<AbstractClass>
directly)。以下是我需要的基本示例:
#include <vector>
class Foo
{
virtual void foo() {}
};
class Bar : public Foo
{
void foo(){}
};
class FooBar
{
vector<Foo*> list;
void add(){ list.push_back(new Bar()); }
};
来自Java,我非常害怕指针和内存泄漏。 This post最初建议在对象超出范围时手动删除对象。在FooBar
的析构函数中执行此操作是否足够?
class FooBar
{
vector<Foo*> list;
void add(){ list.push_back(new Bar()); }
~FooBar(){
for(vector<Foo*>::iterator it=list.begin(); it!=list.end(); it++)
delete *it;
}
};
通过rule of three,我想我还必须实现一个复制构造函数和赋值运算符。以下内容(基于this和that帖子)是否正确实施?
FooBar::FooBar(const FooBar& orig) : list(orig.list.size()) {
try {
vector<Foo*>::iterator thisit = list.begin();
vector<Foo*>::const_iterator thatit = orig.list.cbegin();
for (; thatit != orig.list.cend(); ++thisit, ++thatit)
*thisit = *thatit; // I'm okay with a shallow copy
} catch (...) {
for (vector<Foo*>::iterator i = list.begin(); i != list.end(); ++i)
if (!*i)
break;
else
delete *i;
throw;
}
}
FooBar& operator=(const FooBar& orig){
FooBar tmp(orig);
swap(tmp);
return *this;
}
答案 0 :(得分:1)
据我所知,您的代码存在一些重大缺陷。首先,您的第一个代码是正确的,是的,您的析构函数会执行它应该执行的操作,只要您的类拥有对象并且不会被复制。
此外,您的复制构造函数和赋值运算符看起来有问题。首先,如果您对浅层复制没有问题,那么您甚至不必编写函数,std::vector
的复制构造函数和赋值运算符无论如何都会执行您手动完成的操作。我不认为你需要那个try-catch块。顺便说一句,你的赋值运算符中swap
的实现在哪里?
为了您的目的,一个非常小的参考计数方案将起作用:
class FooPtr
{
Foo* _raw;
size_t* _ctl;
FooPtr(Foo* ptr) :
_raw(ptr), _ctl(new size_t(0))
{
}
template <class T>
FooPtr(T* ptr) : FooPtr(static_cast<Foo*>(ptr)) {}
FooPtr(const FooPtr& rhs) :
_raw(rhs._raw), _ctl(rhs._ctl)
{
++(*_ctl);
}
~FooPtr()
{
if (_raw == nullptr) return;
--(*_ctl);
if (*_ctl == 0)
{
delete _raw;
delete _ctl;
}
}
FooPtr& operator=(FooPtr ptr)
{
std::swap(*this, ptr);
return *this;
}
Foo* operator->()
{
return _raw;
}
Foo& operator*()
{
return *_raw;
}
}
您的班级现在看起来像这样,不需要析构函数,复制程序或赋值运算符:
class FooBar
{
vector<FooPtr> list;
void add(){ list.emplace_back(new Bar()); }
}
我已经快速写了这个,它可能包含错误,虽然它给了你我想的基本想法。此外,我还使用了emplace_back
和nullptr
等一些c ++ 11功能,但它们很容易在非11代码中转换。
答案 1 :(得分:1)
我会在vector
周围写一个小包装器,为你释放元素:
template<class Container>
void delete_elements(Container& cont) {
typedef typename Container::reverse_iterator iterator;
iterator end = container.rend();
for (iterator i = container.rbegin(); end != i; ++i) {
delete *i;
}
}
struct deep_copy {
template<typename T>
T* operator()(T const* const other) {
return other->clone();
}
}
template<typename T>
struct ptr_vector {
std::vector<T*> container;
ptr_vector() { }
ptr_vector(ptr_vector const& other) {
std::vector<T*> tmp;
tmp.reserve(other.container.size());
try {
std::transform(other.container.begin(), other.container.end(),
std::back_inserter(tmp), deep_copy());
}
catch (...) {
(delete_elements)(tmp);
throw;
}
container.swap(tmp);
}
ptr_vector& operator=(ptr_vector other) {
container.swap(other.container);
return *this;
}
~ptr_vector() {
(delete_elements)(container);
}
};
现在您可以使用ptr_vector<Foo> v
并使用v.container
添加元素。注意:您应该在基类中添加clone
成员函数,这样就不会在要复制时进行切片。