我有以下课程:
class Logger {
public:
DEFAULT_CONSTRUCTOR(Logger);
DEFAULT_DESTRUCTOR(Logger);
NO_DEFAULT_COPY_AND_ASSIGN(Logger);
bool Init(std::wstring logFileLocation, std::wstring assemblyVersion);
void Shutdown();
void const LogMessage(std::wstring message);
private:
struct LoggerImpl;
std::unique_ptr<LoggerImpl> impl; // Error here
};
但是,在尝试编译时,我收到以下错误:
// Errors:
// Error 1 error C2027: use of undefined type 'ophRuntime::Logger::LoggerImpl'
// Error 2 error C2338: can't delete an incomplete type c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory
// Warning 3 warning C4150: deletion of pointer to incomplete type 'ophRuntime::Logger::LoggerImpl'; no destructor called c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory
Logger::LoggerImpl
在相应的.cpp文件中有定义:
struct LoggerStream::LoggerStreamImpl {
LoggerStreamImpl(std::wstring componentName) : componentName(componentName) { }
const std::wstring componentName;
NO_DEFAULT_COPY_AND_ASSIGN(LoggerStreamImpl);
};
最后,这些宏的定义:
#define NO_DEFAULT_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
void operator=(const TypeName&) = delete; \
TypeName(TypeName&&) = default; \
#define DEFAULT_CONSTRUCTOR(TypeName) TypeName();
#define DEFAULT_DESTRUCTOR(TypeName) ~TypeName();
答案 0 :(得分:7)
在标题中声明析构函数并在源文件中定义它。
中的.h
class Logger {
public:
Logger(); // default constructor
~Logger(); // default destructor
private:
struct LoggerImpl; // impl forward declaration
std::unique_ptr<LoggerImpl> impl; // impl pointer
};
在.cpp
中// Define the impl structure
struct Logger::LoggerImpl
{
};
// Define the default constructor
Logger::Logger() :impl(std::make_unique<LoggerImpl>())
{
}
// Define the default destructor
Logger::~Logger()
{
}
就是这样。
在您的情况下,您说。{c}中定义了LoggerImpl
,但我没有看到LoggerImpl
的定义,只是LoggerStreamImpl
的定义。
答案 1 :(得分:6)
唯一指针的参数类型必须是翻译单元中的完整类型,您可以在其中实例化唯一指针:
struct Foo;
std::unique_ptr<Foo> p; // Error
问题当然是没有完整类型的Foo
就无法定义唯一指针的析构函数。如果您需要某种拉伸,可以通过将析构函数延迟到单独的翻译单元来解决这个问题:
template <typename T> struct Impl
{
Impl();
~Impl();
std::unique_ptr<T> impl;
};
struct Foo
{
struct FooImpl;
Impl<FooImpl> impl;
};
int main()
{
Foo f;
}
此翻译单元编译,因为Foo::impl
的销毁留给了析构函数Impl<FooImpl>::~Impl<FooImpl>
,它被声明(模板类Impl<FooImpl>
完成) ,它只是定义在目前的翻译单元中不可用。
再次强调所有这一点:此处介绍的pimpl结构允许您将类Foo::FooImpl
的定义放入一个单独的私有翻译单元,类Foo
的消费者永远不会去看。这种解耦的代价是一个额外的动态分配(在Impl<T>
的构造函数中)和一个额外的间接(在Foo
的成员函数的定义中,未显示),以及缺少内联Impl<T>::impl
的析构函数。
有关详细信息,请参阅Herb Sutter的优秀GotW #101。