0

对于自定义使用,我继承std::vector了一个自定义class Vector. 对于我的要求,这种public 继承是可以的

一个目的是避免多次复制向量数组,所以我决定这个自定义类是基于“所有权”的。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // vA2 refers to original A[100] and ...
                     // ... vA1 is now blank which is expected

这是为 C++03 实现的方式:

template<typename T>
struct Vector : std::vector<T>
{
  // ... other constructors

  Vector (const Vector &copy) // copy constructor
  {
    this->swap(const_cast<Vector&>(copy)); // <---- line of interest
  }
  // 'operator =' is TBD (either same as above or unimplemented)
};

我是否违反了任何语言规则/功能/约定?这段代码有什么不好的吗?

编辑:我在下面以答案的形式添加了我的新方法(这是它的工作演示)。

4

4 回答 4

4

我认为您正在寻找的是移动语义。

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = std::move(vA1); // vA2 refers to original A[100] and ...
                 // ... vA1 is now blank which is expected

如果您的编译器没有移动语义支持,我建议您使用智能指针或查看boost::move,它可以在 C++11 之前的编译器中模拟移动语义。

std::shared_ptr<Vector<A>> vA1(new Vector<A>(100)); // vA1 is allocated A[100]
std::shared_ptr<Vector<A>> vA2 = vA1; // vA2 refers to original A[100] and ...
vA1.reset();
                 // ... vA1 is now blank which is expected

甚至更简单:

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2;
vA1.swap(vA2);// vA2 refers to original A[100] and ...
                 // ... vA1 is now blank which is expected
于 2012-09-30T17:06:18.840 回答
3

您真的应该看看使用 C++11:所有标准容器都是可移动的,因此在许多情况下类似于您的语义,即当知道所有权可以转移时。更准确地说,当从函数返回对象或使用临时构造或分配对象时,容器会被移动而不是复制。尝试以某种方式复制这种机制几乎肯定会在某些情况下产生意想不到的行为。

在 C++11 中,可移动语义被集成到语言和函数中,包括构造函数和赋值,可以重载以根据对象是否可移动而表现不同。

于 2012-09-30T17:08:52.047 回答
2

您的代码违反了支持 const 正确性的约定:

const Vector<int> foo(12);
const Vector<int> bar(foo); // UB.
some_function_that_takes_Vector_by_value(bar); // UB.

您可能会说,“但没有人会创建 的const实例Vector,因此 UB 永远不会发生”。在这种情况下,您至少可以让您的复制 ctor 采用非常量引用,以遵循函数不应修改const&参数所采用的对象的约定。

即便如此,我个人更愿意要求用户通过显式交换来“开启”速度,而不是为他们创建这样的陷阱。我也宁愿根本没有复制 ctor,而不是行为与普通副本不同的复制 ctor。如果人们必须记住Vector实际上无法复制,那么他们不应该使用复制语法来不复制它。

C++03 中不必要的副本问题并不是一个新问题,IMO 也不需要新的解决方案。许多 C++ 程序员已经知道如何利用swap和复制省略,而那些不知道如何避免不必要的复制的人最好学习以正常方式进行操作,而不是学习以特定方式要求的独特方式进行操作班级。

于 2012-09-30T17:15:25.440 回答
1

通过评论/答案的洞察力,我意识到我目前的方法不是处理这些事情的好主意。
我想出了一个改变的设计,我尝试使用 swap()方法模拟“移动”操作(在这种情况下考虑它们是可互换的)。

对于任何想要更快复制(即通过交换移动)的类或容器,应该继承下面的类(直接从真实文件复制粘贴):

  /*
   * An empty CRTP base class for the classes which are allowing move sematics (effectively swap)
   * A given class `X` must implement `void swap(X&);` method for the movement (i.e. content swapping)
   * For example, `X` is deriving this class (i.e. `class X : public Movable<X> ...`)
   * ... below is an illustration for how to transfer contents of an `X` object to another
   * `X o1; ... X o2(o1.move()); // contents of o1 is now moved to o2`
   */
  template<typename Derived>
  class Movable
  {
    /*
     * Empty constructor for normal behavior
     */
    public: Movable ()
            {}

    /*
     * Copy constructor which does nothing; without this compiler errors out !
     */
    public: Movable (const Movable &copy)
            {}

    /*
     * Move constructor which will effectively call `Derived::swap()` method
     * After this, the contents of the object will be moved to each other
     * For syntactic sugar, it has been made `explicit`
     */
    public: explicit Movable (Movable &orig)
            {
              (*this)(orig);
            }

    /*
     * Moving while Not initializing object, one need to use `operator ()` and not `operator =`
     * If `operator =` is used then simply `move()` part will not happen; i.e. below 2 are same:
     * `obj1 = obj2.move();` and `obj1 = obj2;`
     */
    public: void operator () (Movable &orig)
            {
              static_cast<Derived*>(this)->swap(static_cast<Derived&>(orig));
            }
    /*
     * This method is called from `Derived` class when move sematics is intended
     */
    public: Movable& move ()
            {
              return *this;
            }
  };

这里应该如何部署:

template<typename T>
struct Vector : std::vector<T>,
                Movable<Vector<T> >  // inherit as CRTP base
{
  // ... other methods
  typedef Movable<Vector<T> > Movable_;
  Vector (Movable_ &orig) : Movable_(orig) {}  // Declare a constructor
};

这里应该如何使用它:

Vector<A> vA1(100); // vA1 is allocated A[100]
Vector<A> vA2 = vA1; // normal copying (no change)
vA2 = vA1; // normal assignment (no change)
Vector<A> vA3(vA1.move()); // <------- "moves" contents from 'vA1' to 'vA3'
vA1(vA2.move()); // <------- "moves" contents from 'vA2' to 'vA1'
vA2 = vA3.move(); // normal copying from 'vA3' to 'vA2' ('vA3' unaffected)
于 2012-10-01T11:31:48.457 回答