【发布时间】:2016-01-16 20:45:44
【问题描述】:
我正在寻找一个很好的例子,说明如何使用移动语义实现派生类和基类。我想得越多,默认移动构造函数和赋值移动运算符似乎就越能正常工作,因为大多数标准(STL)类型和智能指针都是默认可移动的。
无论如何,如果我们有一个需要显式移动实现的类层次结构,我应该怎么做 - 至少作为第一次切割?
在这个例子中,我使用了一个原始指针,我通常会将它包装在 std::unique_ptr 中,但我需要一个移动的示例,它不是默认可移动的。
任何帮助将不胜感激。 :)
目前,我做了以下尝试:
struct BlobA
{
char data[0xaa];
};
struct BlobB
{
char data[0xbb];
};
//----------------------------------------
class Base
{
public:
//Default construct the Base class
//C++11 allows the data members to initialised, where declared. Otherwise you would do it here.
Base()
{
}
//Define the destructor as virtual to ensure that the derived destructor gets called. (In case it is necessary. It's not in this example but still good practice.)
virtual ~Base()
{
delete m_moveableDataInBase; //this is a contrived example to show non-default moveable data, in practice RAII should be used rather than deletes like this
}
//Copy constructor, needs to accept a const ref to another Base class with which it copies the data members
Base(const Base& other) :
m_moveableDataInBase(new BlobA(*other.m_moveableDataInBase)), // copy the other's moveable data
m_pNonMoveableDataInBase(other.m_pNonMoveableDataInBase) // copy the other's non-moveable data
{
}
//Assignment operator uses the canonical copy then swap idiom. It returns a reference to allow chaining: a = b = c
//This is thus implemented in terms of the copy constructor.
Base& operator=(const Base& rhs)
{
Base temp(rhs);
Swap(temp);
return *this;
}
//The move construtor is declared as throwing no exceptions so that it will be called by STL algorithms
//It accepts an rvalue and moves the Base part.
Base(Base&& other) noexcept :
m_moveableDataInBase(nullptr) // don't bother allocating our own resources to moveable data because we are about to move (steal) the other's resource
{
Swap(other);
}
//The move assignment operator is declared as throwing no exceptions so that it will be called by STL algorithms
//It accepts an rvalue and moves (steals) the data resources from the rhs using swap and copies the non moveable data from the rhs
Base& operator=(Base&& rhs) noexcept
{
//move (steal) the moveable contents from rhs
std::swap(m_moveableDataInBase, rhs.m_moveableDataInBase);
//copy the non-moveable contents from rhs
m_pNonMoveableDataInBase = rhs.m_pNonMoveableDataInBase;
return *this;
}
private:
//this private member swaps the data members' contents.
//It is private because it isn't virtual and only swaps the base contents and is thus not safe as a public interface
void Swap(Base& other)
{
std::swap(m_moveableDataInBase, other.m_moveableDataInBase);
std::swap(m_pNonMoveableDataInBase, other.m_pNonMoveableDataInBase);
}
//an example of some large blob of data which we would like to move instead of copy for performance reasons.
//normally, I would have used a unique_ptr but this is default moveable and I need an example of something that isn't
BlobA* m_moveableDataInBase{ new BlobA };
//an example of some data that we can't or don't want to move
int m_pNonMoveableDataInBase = 123;
};
//----------------------------------------
class Derived : public Base
{
public:
//Default construct the Derived class, this is called after the base class constructor
//C++11 allows the data members to initialised, where declared. Otherwise you would do it here.
Derived()
{
}
//Default virtual destructor, to clean up stuff that can't be done automatically through RAII
virtual ~Derived()
{
delete m_pMoveableDataInDerived; //this is a contrived example to show non-default moveable data, in practice RAII should be used rather than deletes like this
}
//Copy constructor, needs to accept a const ref to another derived class with which it
//first copy constructs the base and then copies the derived data members
Derived(const Derived& other) :
Base(other), // forward to the base copy constructor
m_pMoveableDataInDerived(new BlobB(*other.m_pMoveableDataInDerived)), // copy the other's moveable data
m_pNonMoveableDataInDerived(other.m_pNonMoveableDataInDerived) // copy the other's non-moveable data
{
}
//Assignment operator uses the canonical copy then swap idiom. It returns a reference to allow chaining: a = b = c
//Because it uses the derived copy constructor, which in turn copy constructs the base, we don't forward to the base assignment operator.
Derived& operator=(const Derived& rhs)
{
Derived temp(rhs);
Swap(temp);
return *this;
}
//The move construtor is declared as throwing no eceptions so that it will be called by STL algorithms
//It accepts an rvalue and first moves the Base part and then the Derived part.
//There is no point in allocating any resource before moving so in this example, m_pBlobB is set to nullptr
Derived(Derived&& other) noexcept
: Base(std::move(other)), // forward to base move constructor
m_pMoveableDataInDerived(nullptr) // don't bother allocating our own resources to moveable data because we are about to move (steal) the other's resource
{
Swap(other);
}
//The move assignment operator is declared as throwing no exceptions so that it will be called by STL algorithms
//It accepts an rvalue and first calls the base assignment operator and then moves the data resources from the rhs using swap
Derived& operator=(Derived&& rhs) noexcept
{
//forward to the base move operator=
Base::operator=(std::move(rhs));
//move (steal) the moveable contents from rhs
std::swap(m_pMoveableDataInDerived, rhs.m_pMoveableDataInDerived);
//copy the non-moveable contents from rhs
m_pNonMoveableDataInDerived = rhs.m_pNonMoveableDataInDerived;
}
private:
//this member swaps the Derived data members contents.
//It is private because it doesn't swap the base contents and is thus not safe as a public interface
void Swap(Derived& other) noexcept
{
std::swap(m_pMoveableDataInDerived, other.m_pMoveableDataInDerived);
std::swap(m_pNonMoveableDataInDerived, other.m_pNonMoveableDataInDerived);
}
//an example of some large blob of data which we would like to move instead of copy for performance reasons.
//normally, I would have used a unique_ptr but this is default moveable and I need an example of something that isn't
BlobB* m_pMoveableDataInDerived{ new BlobB };
//an example of some data that we can't or don't want to move
int m_pNonMoveableDataInDerived = 456;
};
【问题讨论】:
-
您是否考虑过使用
unique_ptr<Bla>作为可移动数据指针?然后你会“免费”获得很多语义 -
@MM,是的,我在我的问题中指出了这一点,如果您可以花时间阅读它。谢谢。
-
我不明白您在包含“unique_ptr”一词的句子中要说什么。你是说你选择不使用 unique_ptr 即使你知道它会解决你的问题?