gpt4 book ai didi

c++ - 这是在 C++11 中使用 unique_ptr 和移动语义实现 pimpl 的正确方法吗

转载 作者:可可西里 更新时间:2023-11-01 16:41:10 26 4
gpt4 key购买 nike

我还没有看到同时使用 unique_ptr 和移动语义的 pimpl 示例。

我想向 STL 派生容器添加一个 CHelper 类,并使用 pimpl 隐藏 CHelper 的功能。

这样看起来对吗?

派生.h

class CDerived : public set<CSomeSharedPtr>, public CHelper  
{
//...
};

`

Helper.h

// derived containers need to support both copy and move, so CHelper does too  

class CHelper
{
private:
class impl;
unique_ptr<impl> pimpl;

public:
//--- default: need both cotr & cotr (complete class) in order to use unique_ptr<impl>
CHelper();
~CHelper();

//--- copy
CHelper(const CHelper &src); //copy constructor
CHelper& operator=(const CHelper &src);//assignment operator

//--- move
CHelper(CHelper &&src); //move constructor
CHelper& operator=(CHelper &&src);//move operator

//--- expose public methods here
void SetModified(BOOL bSet=TRUE);
};

助手.cpp

//===========================  
class CHelper::impl
{
public:
BOOL m_bModified; //has the container been modified (needs to be saved)
// ... other data

impl() {m_bModified = FALSE;}

//--- copy cotr/assign
impl(const impl &src)
{
*this = src;
}

void operator=(const impl &src)
{
m_bModified = src.m_bModified;
// ...other data
}

//--- move cotr/assign ?? do I need to write move cotr/assign ??

};

//============================
CHelper::CHelper() : pimpl(unique_ptr<impl>(new impl())) {}

CHelper::~CHelper() {}

//--- copy
CHelper::CHelper(const CHelper &src)
: pimpl(unique_ptr<impl>(new impl(*src.pimpl))) {}

CHelper& CHelper::operator=(const CHelper &src)
{
if (this != &src)
*pimpl = *src.pimpl;

return *this;
}

//--- move
CHelper::CHelper(CHelper &&src)
{
if (this != &src)
{
pimpl = move(src.pimpl); //use move for unique_ptr
src.pimpl.reset(nullptr);//leave pimpl in defined / destructable state
}
}

CHelper& CHelper::operator=(CHelper &&src)
{
if (this != &src)
{
pimpl = move(src.pimpl); //use 'move' for unique_ptr
src.pimpl.reset(nullptr);//leave pimpl in defined / destructable state
}
return *this;
}

最佳答案

考虑到 CHelper 的唯一成员是 unique_ptr,并且复制的默认实现是调用基和成员的拷贝,移动 的默认实现是调用 基和成员的移动,无需覆盖 CHelper 构造函数和赋值。让默认值完成它们的工作。他们只会调用适当的 unique_ptr 移动构造函数和运算符。

关于将 CHelperset<...> 放在一起形成 CDerived...这不是“规范设计”(set 不是 OOP 类...,CHelper 也不是)但如果使用得当(不要不要尝试将一个 CDerived 分配给一个 CHelper* 广告调用 delete on it,否则你会泪流满面)。只是没有足够的信息来理解它们的用途。

如果问题是“我希望 CHelper 也能够复制”,那么你应该更好遵循这样的成语(#includeusing namespace 分开......)

class CHelper
{
struct impl
{
.....
};
public:
// create and initialize
CHelper() :pimpl(new impl) {}

// move: just keep the default
CHelper(CHelper&& a) = default;

// copy: initialize with a copy of impl
CHelper(const CHelper& a) :pimpl(new impl(*a.pimpl)) {}

CHelper& operator=(CHelper a) //note: pass by value and let compiler do the magics
{
pimpl = move(a.pimpl); //a now nullifyed, but that's ok, it's just a value
return *this;
}

~CHelper() = default; //not really necessary
private:
unique_ptr<impl> pimpl;
};

当然,您可以根据需要随意分离声明和实现。

根据 John Balcom 的评论进行编辑。

是的,代码当然会改变,但不会改变其实质。您只需将 struct impl; 转发声明为 CHelper(以便 unique_ptr 有意义),然后声明struct CHelper::impl 在其他地方(可能在 CPP 文件中,所有 CHelper 实现都将在该文件中完成)。

这里唯一需要注意的是,在 CPP 文件中,CHelper::impl 必须同时定义构造函数和析构函数,以便在 CPP 文件中具有连贯的 unique_ptr 实例化(必须调用 impl 析构函数)。否则,对于某些编译器,存在在包含 CHelper 声明的所有文件中出现“不完整类型使用”错误的风险。

关于第二点(源自 std::set ),这是 C++ 编程的一个有争议的方面。出于与 C++ 本身无关的原因,但在面向对象编程学派中,“inhertance” 意味着 “是一个”“是一个” 表示“可能的对象替换”。因此,如果基 dtor 不是虚拟的,则通过基指针删除对象是 UB,从而使对象替代 UB,OOP 学校拒绝作为教条继承任何没有虚拟 dtor 的类,并且因为这种方式他们在开始编程时就已经受过教育,如果你这样做,他们就会开始向你吐火。

对我来说,这不是您设计中的问题,但他们错误地理解 C++ 继承并不意味着“是一个”而是“就像”并且确实如此不暗示对象替换(对我来说,他们认为每个 C++ 类都是 OOP 对象是他们的错,而不是你使用工具来做对你有用的事情,只要看看 herehere ,如果你想进一步澄清我的立场:对象替换在 C++ 中不是针对“对象”,而是针对方法的方法,因为每个方法都可以是虚拟的,也可以不是彼此独立的)。就是说,也许您必须与这些人一起工作,所以...评估不跟随他们奉行他们自己喜欢的宗教的利弊。

关于c++ - 这是在 C++11 中使用 unique_ptr 和移动语义实现 pimpl 的正确方法吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11211952/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com