使用移动语义和对抽象类的子项的引用向量

时间:2016-09-11 20:33:15

标签: c++ class oop move-semantics raii

我正在尝试(不成功地)创建一个对象向量并使用polimophism而不需要手动内存管理和依赖raii习惯用法的任何类型的指针(*,unique_ptr,shared_ptr,weak_ptr等)。

#include <iostream>
#include <vector>
using namespace std;

struct A{
    virtual void foo()=0;
    virtual ~A(){}
};
struct C:A{
    int bar;
    C(int bar):bar(bar){}
    virtual void foo(){
        cout << bar << endl;
    }
    virtual ~C(){}
};
struct B:A{
    vector<A> &&bar;
    int f;
    B(int f, vector<A> bar):f(f),bar(bar){}
    B(int f, vector<A> &bar):f(f),bar(bar){}
    B(int f, vector<A> &&bar):f(f), bar(bar){}
    virtual void foo(){
        cout << f << endl;
        for(auto &f: bar){
            f.foo();
        }
    }
    virtual ~B(){}
};

int main() {
    B b{1, vector<A>{C{1},C{2}}};
    b.foo();
    return 0;
}

但它没有编译。我做错了什么?

1 个答案:

答案 0 :(得分:2)

std::vector<A> something

是一个向量,在添加对象时为AA动态分配存储空间。

向量的存储类型为A,它存储A的实例。

当您将第一个C推到它上面时,它会执行以下操作:

void push_back(const A& a)  // NOTE: A, not C, downcasting occurs
{
    // extreme aproximation
    if (something.begin_ == nullptr) {
      something.begin_ = new A;
      something.begin_[0] = a;  // A::operator=(const A&);
      something.capacity_ = something.end_ = something.begin_ + 1;
    } else {
      ..
    }
}

由于C继承自A,因此可以向下转发。但是,它只会复制传递给它的对象的A部分,因为向量的存储类型是A ,因此您的int bar C中未复制。

这称为切片,它的唯一方法是动态(指针或std :: any),或使用联合的危险路径。

- 编辑 -

您希望std::vector<A> &&bar做的第二个问题是什么?它引用的临时对象在构造函数表达式的末尾到期:

B b{1, vector<A>{C{1},C{2}}};  // temporary vector is destroyed
// b.bar is dangling rvalue reference

- 编辑2 -

您的问题标题包含&#34;使用移动语义&#34;但如编辑1中所述,您只存储右值参考。移动语义是关于转移的:考虑这个简单的strdup包装器:

struct Strdupper {
    char* str_;
    Strdupper() = delete;
    Strdupper(const char* str) : str_(strdup(str)) {}
    ~Strdupper() { if (str_) free(str_); }
    Strdupper(const Strdupper& rhs) { str_ = strdup(rhs.str_); }
    Strdupper(Strdupper&& rhs) str_(rhs.str_) { rhs.str_ = nullptr; }
    Strdupper& operator=(const Strdupper& rhs) { if (str_) free(str_); str_ = strdup(rhs.str_); return *this; }
    Strdupper& operator=(Strdupper&& rhs) { std::swap(str_, rhs.str_); return *this; }
    // ...
};

而不是rvalue引用,你的类B需要实际存储向量并将std::move输入参数值存储到其构造函数中以调用它的move-constructor:

class B {
    std::vector<X> vec_;

public:
    B() = default;
    B(const std::vector<X>& vec) : vec_(vec) {}  // copy semantics
    B(std::vector<X>&& vec) : vec_(std::move(vec)) {}  // move semantics
};