C ++使用未定义类型

时间:2014-01-25 23:28:29

标签: c++

我有以下课程:

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();

2 个答案:

答案 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