如何禁用从派生类移动构造基类?

how to disable move construct base class from derived class?

提问人:Nolazuck 提问时间:4/21/2022 更新时间:4/21/2022 访问量:168

问:

在下面的代码中,我想从派生类中禁用基类的移动构造,并调用复制构造函数。VectorVectorMap

#include <iostream>
#include<algorithm>

struct Vector{
    int* _ptr=nullptr;
    int _size=0;
    Vector(int n){
      _ptr = new int[n];
      _size = n;
      std::cout<< " Construct "<<this<<std::endl;
    }

    Vector(void) { std::cout <<" Construct " << this << std::endl; }

    virtual ~Vector(void) {
      if (_ptr != nullptr) {
        std::cout << "Deconstruct " << this << " -> delete " << _ptr << std::endl;
        delete _ptr;
        return;
      }
      std::cout << "Deconstruct " << this << std::endl;
    }

    Vector(Vector&& v2) noexcept {
        int* p2=v2._ptr; int s2=v2._size;
        v2._ptr=_ptr;
        v2._size=_size;
        _ptr=p2; _size=s2;
        std::cout << "Move construct " << this << std::endl;
    }

    Vector(const Vector& v3){
        _ptr=new int[v3._size];
        _size=v3._size;
        memcpy(_ptr,v3._ptr,sizeof(int) * _size);
    }
};

struct VectorMap
    : public Vector {
        VectorMap(int* p,int size){
            _ptr=p;
            _size=size;
        }
        ~VectorMap(void) override {
            _ptr=nullptr; _size=0;
        }
};

int main(void) {
    Vector v1(10);
    Vector v2=VectorMap(v1._ptr,5);  // v1._ptr will be deleted twice 
    return sizeof(v2);
}

如您所见,如果在行中调用 move 构造函数,则 中的数据指针将被删除两次,一次是 ,另一次是 。因为它们共享相同的指针。在这种情况下,有没有办法修改为调用复制构造函数而不是移动构造函数?Vector v2=VectorMap(v1._ptr,5);v1v2v1VectorMap

C++ 复制构造函数 基类 move-constructor

评论

0赞 Ted Lyngmo 4/21/2022
无关:Re:和 - 从那里删除。它没有任何用途,只是看起来很糟糕。~Vector(void)~VectorMap(void)void
0赞 Nolazuck 4/21/2022
感谢您的申请,我的意思是我应该更改而不是?move constructorVectorMap

答:

0赞 eerorika 4/21/2022 #1

主要问题是所有权语义混乱。

Vector创建一个资源(动态数组),并且显然旨在对其拥有唯一的所有权。

VectorMap另一方面,在其构造函数中获取指针,并将其提供给获取所有权的基础。但在销毁之前,通过将基指针设置为 null 来撤销所有权。因此,在某种程度上假装拥有资源,直到所有权的责任到来,这时它退缩了。VectorVectorMapVectorVectorMap

此继承还会导致其他问题情况。例如,考虑制作 .看看 base 的复制构造函数是做什么的。它为副本分配内存。但是副本的 desturctor 将指针设置为 null,因此在这种情况下,指针被删除零次。它会泄漏内存。VectorMapVectorMap

打个比方,禁用切片将是伤口的绷带,但你真正应该做的是不要把手伸进正在运行的搅拌机里。如果应该没有所有权,那么它不应该继承一个拥有所有权的基础。我甚至不清楚上课的意义是什么。VectorMapVectorMap

此外,对资源具有唯一所有权的类(如 real)应使用私有访问说明符封装该裸指针。当然,这样的类有时仍然提供了一种复制或丢弃该值的方法(如 和 ),但重要的是,它只能通过特定函数发生,以减少意外违反所有权语义的机会。Vectorstd::vector::datastd::unique_ptr::release


另一个严重的错误:

_ptr=new int[v3._size];
// ...
delete _ptr;

不能是指向动态数组的指针。您必须使用 .使用会导致未定义的行为。deletedelete[]delete


另一个错误:您忘记包含声明 .另外,我建议改用。memcpystd::copy

评论

0赞 Nolazuck 4/21/2022
非常感谢,看来我忽略了太多问题。但正确的设计应该是什么?我想要一个采用指针和大小的类,并且像不分配新内存一样。VectorMapVector
0赞 eerorika 4/21/2022
@Nolazuck 听起来你想要的是.std::span
0赞 Nolazuck 4/21/2022
好的,我会努力的。