C++ 复制构造函数清除 向量<基*> 派生*
C++ copy constructor clear up on vector<Base*> of Derived*
我有一个类,它使用一类指向派生对象的基指针,所以我需要有自己的构造函数来删除向量的元素以及自定义的复制和赋值函数。我不完全确定实现下面这样的结构并为其编写正确的复制和赋值构造函数和析构函数的首选方法。我可以请你指导我吗?我读了很多书,搜索了很多,但我还是不确定。
class Base
{
public:
Base();
~virtual Base();
int a;
int type; // Derived1 or Derived2
std::string b;
...
}
class Derived1 : public Base
{
public:
Derived1();
Derived1(const Base* a);
virtual ~Derived1();
}
class Derived1
{
Derived1::Derived1(const Base *a) : Base(a)
{
}
}
class Derived2 : public Base
{
public:
Derived2();
Derived2(const Base* a);
virtual ~Derived2();
std::string d1;
std::string d2;
int d3;
}
class Derived2
{
Derived2::Derived2(const Base *a) : Base(a) {
this->d1 = ((Derived2*)a)->d1;
this->d2 = ((Derived2*)a)->d2;
this->d3 = ((Derived2*)a)->d3;
}
}
class A
{
public:
A();
~A();
A(const A& a);
A& operator = (const A& a);
std::string someString;
std::vector<Base*> vect;
}
A::~A() {
std::vector<Base*>::iterator it = vect.begin();
while (it != vect.end()) {
delete (*it);
it++;
}
A::A(const A &a)
{
someString = a.someString;
for(std::size_t i = 0; i < a.vect.size(); ++i {
someString = a.someString;
Base* base = a.vect.at(i);
if(base->type == base::TypeD1) {
base = new Derived1( a.vect.at(i) );
vect.push_back( base );
}
else {
base = new Derived2( a.vect.at(i) );
vect.push_back( base );
}
}
}
析构函数中的循环在实践中很好通常的解决方案。从形式上讲,这是未定义的行为,因为正在将对象留在矢量中(指向已删除对象的指针)它们是不可复制的,但在实践中:矢量不会复制除非您将其调整为更大的大小,或者插入或如果你真的想避免未定义的行为:
for ( auto current = vect.begin(); current != vect.end(); ++ current ) {
Base* tmp = *it;
*it = nullptr;
delete tmp;
}
但这是一个我可能不会打扰的情况(以及我对未定义的行为往往比大多数人更敏感)。
首先,您真的需要复制和分配类型为A
的对象吗?如果没有,简单的解决方案是:
class A
{
public:
A();
~A();
A(const A&) = delete;
A(A&&) = default;
A& operator=(const A&) = delete;
A& operator=(A&&) = default;
// ...
};
如果是,那么您需要一些多态的方式来复制向量的元素。(任何时候,只要你有if (b->type == Base::TypeD1) { do_this(); } else { do_that(); }
,停下来思考为do_this
/do_that
添加一个虚拟函数是否有意义。否则,如果heimer的方法不允许未来的新派生类,那么虚拟方法会。)
class Base
{
public:
// ...
virtual Base* clone() const = 0;
// ...
};
class Derived1 : public Base
{
public:
virtual Derived1* clone() const;
};
Derived1* Derived1::clone() const {
return new Derived1(*this);
}
A
的复制赋值操作符需要像析构函数一样销毁lhs的旧内容,然后像复制构造函数一样复制新内容。让我们把这两个操作放在私有函数中:
class A
{
public:
A();
~A();
A(const A&);
A(A&&) = default;
A& operator=(const A&);
A& operator=(A&&) = default;
// ...
private:
void destroy_contents();
void copy_from(const std::vector<Base*>& v);
};
void A::destroy_contents() {
std::vector<Base*>::iterator it = vect.begin();
while (it != vect.end()) {
delete (*it);
++it;
}
vect.clear();
}
void A::copy_from(const std::vector<Base*>& v) {
std::vector<Base*>::const_iterator it = v.begin();
while (it != v.end()) {
vect.push_back((*v)->clone());
++it;
}
}
A::~A() { destroy_contents(); }
A::A(const A& a) :
someString(a.someString),
vect()
{
copy_from(a.vect);
}
A& A::operator=(const A& a) {
if (this != &a) {
someString = a.someString;
destroy_contents();
copy_from(a.vect);
}
return *this;
}
如果您有一个任何指针类型的矢量,该矢量对指针后面的类型一无所知,也不在乎:矢量只对指针进行操作,如果需要,将它们复制到新位置,但永远不会接触对象本身。因此,销毁这些物品本身是你的责任。
正如James Kanze所指出的,在处理无效指针时,存在未定义行为的轻微危险。然而,由于vector
在删除其所包含的对象后不会以任何方式使用,因此在您给定的代码中不会调用未定义的行为(矢量不需要重新分配内存,因此不需要分配无效的指针,指针的销毁是一个问题)。因此,class A
的析构函数非常好。
然而,class A
的复制构造函数不必要地复杂,并且是大量错误的来源(每当定义新的派生类时都需要更新它!)。最好的方法是使用clone()
函数:
class Base {
public:
//...
virtual Base* clone() const = 0; //Returns a new copy of the object. Pure virtual if the Base class is abstract.
};
class Derived1 : public Base {
public:
Derived1(const Derived& a);
virtual Derived* clone() const {
return new Derived1(*this);
}
};
如果您在基类中使clone()
纯虚拟,那么您可以保证,如果您忘记在任何派生类中实现它,编译器都会抱怨。这样一来,class A
的复制构造函数就变得微不足道了:
A::A(const A &a) {
someString = a.someString;
for(std::size_t i = 0; i < a.vect.size(); ++i {
vect.push_back(a.vect[i]->clone());
}
}
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 使用strcpy将char数组的元素复制到另一个数组
- 是否可以初始化不可复制类型的成员变量(或基类)
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 复制列表初始化的隐式转换的等级是多少
- 当从函数参数中的临时值调用复制构造函数时
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 复制几乎为空的数组的最快方法
- 以下示例中如何避免代码复制?C++/库达
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 不能将复制初始化与隐式转换的多个步骤一起使用
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- 为什么复制而不是移动数据元素?
- 文件系统:复制功能的速度秘诀是什么
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- EASTL矢量<向量<int>>连续的
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy