我正在使用pimpl-idiom std::unique_ptr:
class window {
window(const rectangle& rect);
private:
class window_impl; // defined elsewhere
std::unique_ptr<window_impl> impl_; // won't compile
};
Run Code Online (Sandbox Code Playgroud)
但是,我在第304行的第304行收到有关使用不完整类型的编译错误<memory>:
'
sizeof'到不完整类型'uixx::window::window_impl的应用无效' '
据我所知,std::unique_ptr应该可以使用不完整的类型.这是libc ++中的错误还是我在这里做错了什么?
首先阅读Herb's Sutters GotW关于C++ 11中pimpl的帖子:
我在理解GotW#101中提出的解决方案时遇到了一些麻烦.据我所知,在GotW#100中辛苦解决的所有问题都复仇了:
的pimpl成员是外的线的模板,并且定义并不在使用点可见(在class widget的类定义和隐式生成的特殊成员函数widget).也没有任何明确的实例化.这将导致链接期间未解决的外部错误.
widget::impl在实例化定义的点上仍然是不完整的(我认为它实际上根本没有pimpl<widget::impl>::~pimpl()被实例化,只是被引用).因此std::unique_ptr<widget::impl>::~unique_ptr()调用delete指向不完整类型的指针,如果widget::impl有一个非平凡的析构函数,则会产生未定义的行为.
请解释是什么迫使编译器在widget::impl完成的上下文中生成特殊成员.因为我看不出它是如何工作的.
如果GotW#101仍然需要widget::~widget()在实现文件中明确定义,哪里widget::impl完成,那么请解释"更健壮"的评论(@sehe在他的答案中引用).
我看的GotW#101的核心要求是,包装"消除样板的一些作品",这在我看来(基于该段的其余部分)来表示的widget::~widget()声明和定义.所以请不要依赖于你的答案,在GotW#101中,那已经消失了!
Herb,如果你停下来,请告诉我是否可以在这里剪切+粘贴解决方案代码以供参考.
我还没有看到一个同时使用unique_ptr和move-semantics的pimpl示例.
我想将一个CHelper类添加到STL派生容器中,并使用pimpl来隐藏CHelper所做的事情.
这看起来不错吗?
class CDerived : public set<CSomeSharedPtr>, public CHelper
{
//...
};
Run Code Online (Sandbox Code Playgroud)
`
// 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 …Run Code Online (Sandbox Code Playgroud) 假设我有一个私有成员的类,这是该类客户端不关心的实现细节.这个类是一个值类型,我们希望它是可复制的,例如
#include <boost/bimap.hpp> // some header that pulls in many other files
class MyClass {
public:
MyClass() {}
...
private:
boost::bimap<Key,Value> table;
};
Run Code Online (Sandbox Code Playgroud)
现在,MyClass的每个客户端都被迫引入了许多不需要的升级头,增加了构建时间.但是,该课程至少是可复制的.
如果我们引入编译器防火墙(Pimpl idiom),那么我们可以将#include依赖项移动到cpp文件,但是由于规则5,我们现在必须做更多的努力工作:
// no extra #includes - nice
class MyClass {
public:
MyClass() {}
// ugh, I don't want this, just make it copyable!
MyClass(const MyClass& rhs);
MyClass(MyClass&& rhs);
MyClass& operator=(const MyClass& rhs);
MyClass& operator=(MyClass&& rhs);
~MyClass() {}
...
private:
std::unique_ptr<MyClassImpl> impl;
};
Run Code Online (Sandbox Code Playgroud)
是否有一种技术可以获得编译器防火墙的好处,但保留可复制性,以便我不需要包含5的规则样板?
std::unique<B>我为incomplete type创建了一个小测试用例B。
测试.h
#pragma once
#include <memory>
class B; //<--- compile error here
class Test{
std::unique_ptr<B> bPtr;
//#1 need to move destructor's implementation to .cpp
public: ~Test();
};
Run Code Online (Sandbox Code Playgroud)
测试.cpp
#include "Test.h"
class B{};
Test::~Test(){} //move here because it need complete type of B
Run Code Online (Sandbox Code Playgroud)
主程序
#include <iostream>
#include "Test.h"
using namespace std;
int main(){
Test test;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到此错误:-
/usr/include/c++/4.8.2/bits/unique_ptr.h:65:22:错误:“sizeof”对不完整类型“B”的无效应用
据我了解,编译器告诉我这 B是一个不完整的类型(in main.cpp),因此它无法B正确删除。
但是,在我的设计中,我不想main.cpp拥有完整的B.
粗略地说,这是一个粉刺。
有没有好的解决方法?
这里有一些类似的问题,但没有一个提出干净的解决方法。 …
AFAIKunique_ptr与 PIMPL 一起使用非常棘手,因为删除器是unique_ptr类型的一部分,因此它不适用于不完整的类型。另一方面,shared_ptr使用动态删除器,因此它可以处理不完整的类型。
shared_ptr无论我是否需要,都存在给我原子操作的性能问题。
我可以使用其他更快的替代方案吗std::?我显然对类型擦除很满意,我说的是原子引用计数的成本。
#include <any>
#include <memory>
#include <iosfwd>
std::shared_ptr<std::fstream> sp;
// unique_ptr requires complete type
// std::unique_ptr<std::fstream> up;
std::any a;
#include <fstream>
int main() {
// any requires copy_constructible
// a = std::fstream{};
sp = std::make_shared<std::fstream>();
}
Run Code Online (Sandbox Code Playgroud)
笔记:
any,但它不适用于某些类型。unique_ptr函数永远不会“告诉”删除器构造的对象是什么(为删除器提供一种学习如何销毁对象的方法)。PS 我知道很久以前boost::shared_ptr就有宏来禁用原子引用计数,但即使仍然支持我也不想切换到boost::shared_ptr.