更改对象的类:可以使用std :: move?

时间:2017-05-09 16:08:57

标签: c++ c++11

我知道一旦创建了一个对象,我就不会乱用V-Table(以一种有点理智的方式)。这意味着我必须复制一个对象来改变它的类型。这对c ++ 11的std :: move和朋友来说也适用吗?

class Base {
  public:
    virtual int type() = 0;

    // more data members I want to avoid to copy
};

class D1 : public Base {
  public:
    int type() {
      return 1;
    }
};

class D2 : public D1 {
  public:
    int type() {
      return 2;
    }
};


int main()
{
  // creating the actual object, type is D1
  D1* obj = new D1();

  // this is what does not work, I want to "change" the object to D2
  D2* obj2 = &std::move<D2>(*obj);

  // cast it to obj2 base class 
  Base* baseObj = static_cast<D1*>(obj2);

  // now I want a "2" here
  int t = baseObj->type();
  printf ("%d\n", t);
}

我不太清楚移动语义......但是有什么东西可以将D1对象改为D2(反之亦然)类型安全吗? (这两个类在内存布局上几乎相同)

2 个答案:

答案 0 :(得分:4)

虽然您无法更改现有对象的类型,但您可以轻松更改指针成员的动态类型并获得所需的效果。这称为策略设计模式

E.g:

#include <memory>
#include <iostream>

class Host
{
    struct Strategy
    {
        virtual ~Strategy() = default;
        virtual int type() const = 0;
    };

    struct StrategyA : Strategy { int type() const override { return 1; } };
    struct StrategyB : Strategy { int type() const override { return 2; } };

    std::unique_ptr<Strategy> strategy_;

public:
    Host()
        : strategy_(new StrategyA)
    {}

    int type() const { return strategy_->type(); }

    void change_strategy(int type) {
        switch(type) {
        case 1: strategy_.reset(new StrategyA); break;
        case 2: strategy_.reset(new StrategyB); break;
        default: std::abort();
        }
    }
};


int main() {
    Host host;
    std::cout << host.type() << '\n';
    host.change_strategy(2);
    std::cout << host.type() << '\n';
}

答案 1 :(得分:1)

在我看来,你并不熟悉std::move实际上做的事情。

正如其他人所说,std::move实际上并没有移动任何东西。它从xvalue(基本上是命名变量)获得lvalue(具有名称,但可以将其资源重用或转移到另一个对象的变量)的引用,因此,std::move只不过是一个演员。它不会创建任何新对象。详细了解herehere

仍然关于移动语义主题, std::move非常有用,因此您可以强制rvalue-aware方法接收和重用您绝对知道可以移动其资源的变量中的资源。 为了更深入地了解这一点,我建议您阅读What are move semantics?。例如,它的一个用途是从临时创建一个对象(例如,在函数内创建然后返回的对象):

#include <vector>
#include <iostream>

class A {
  public:
    // A very large resource
    std::vector<int> resource;

    // Constructs our very large resource
    A(): resource(1024, 0) { std::cout << "Default construct" << std::endl; }

    // Move (reuses) a very large resource from an instance
    A(A && other) : resource(std::move(other.resource)) { 
      std::cout << "Move construct" << std::endl;
    }
};

现在,A移动构造函数仅在other对象为rvalue(或xvalue)时调用,例如:

A foo(A a) { return a; }
int main() {
  A a = foo(A());
  return 0
}

在这种情况下,在调用foo()之前,会创建一个临时A()并作为参数传入。 foo()会返回它,但由于它是临时的,因此它适合rvalue并在构造A时传递给A a = foo(A())的移动构造函数。

A()的移动构造函数中,std::move(other.resource)用于构造另一个resource以调用std::vector的移动构造函数,以便它可以尝试使用它可以使用的任何内容来自other.resource,而不是从头开始创建所有内容(然后再复制)。

但如前所述,std::move本身并没有移动任何内容,但它可以传达 intent 来移动,帮助编译器做正确的事(和其他程序员阅读它并更快地理解你的意思。)

所以,要直接回答你的问题,不,除了构建一个新对象之外,没有任何东西可以让你将一个对象转换成另一个对象。如果您确定要销毁obj(顺便说一下,您没有这样做),则可以实现D2的构造函数,该构造函数接受rvalue D1 }}:

class D2 {
  public:
    D2(D1 && d1) : resource(std::move(d1.resource)) { d1.resource = nullptr; }
}

int main() { 
  D1 * obj = new D1();
  D2 * obj2 = new D2(std::move(*obj));
  delete obj;
}

但是,在执行此操作时还需要考虑其他事项,例如移动对象的析构函数和其他详细信息。我建议您阅读有关该主题的更多信息,也可以使用不同的方法来实现您正在做的事情,例如另一个答案中提到的策略模式。