源文件中的Typedef私有struct原型

时间:2013-12-16 20:27:50

标签: c++ pointers implementation pimpl-idiom

在我的课程中,我需要保留一个指向结构的指针,该结构在我用来实现它的库中定义。由于此库仅在实现文件中使用,因此我希望避免将其直接包含在头文件中。同时我想避免污染命名空间。我想这样做:

/* HEADER */
class Foo {
    private:
        struct ImplementationDetail;
        ImplementationDetail * p;
};
/* SOURCE */
#include <Library.h>
using Foo::ImplementationDetail = Library::SomeStruct;

但这不起作用,我现在正在回归PIMPL:

/* HEADER */
class Foo {
    private:
        struct ImplementationDetail;
        ImplementationDetail * p_;
};
/* SOURCE */
#include <Library.h>
struct ImplementationDetail {
    Library::SomeStruct * realp_; 
}

有没有办法避免双重解除引用?由于未知指针大小,我的非工作第一解决方案的原因是什么?

5 个答案:

答案 0 :(得分:1)

// Header
class Foo {
    private:
        struct ImplementationDetail;
        ImplementationDetail * p;
};

// Source
#include <Library.h>
struct Foo::ImplementationDetail :public Library::SomeStruct {
   // ....
};

并且在此源文件中分配/解除分配/解除引用指针应该只能正常工作。

答案 1 :(得分:0)

这是一个不正确的声明:

using Foo::ImplementationDetail = Library::SomeStruct;

using无法正常工作。在C ++ 11中,using无法为一个名称空间中的名称创建另一名称空间中名称的别名。在C ++ 03中,所有using都会在当前翻译单元中将一些其他命名空间带入全局可见性。它不是用来在C ++ 03中创建别名,因为你似乎想在这里做。

pimpl 是执行您要执行的操作的事实上的方法,但是在您的头文件中而不是尝试使用ImplementationDetail*,我会使用简单{ {1}}。根据标准,以这种方式使用void*保证是正确的:

void*

使用class Foo { private: void * pImpl; 2 static_cast转到您的实际类型:

void*

您可以通过向前声明库类型来避免以一致的方式使用void Foo::Bar() { Library::SomeStruct* thingy = static_cast <Library::SomeStruct*> (pImpl); // ... }

void*

然后在实施中不需要丑陋的演员阵容。


2 使用namespace Library { struct SomeStruct; }; class Foo { private: Library::SomeStruct* pStruct; }; :或static_cast

答案 2 :(得分:0)

你不能采取第一种方法的原因是在标题中你告诉编译器“我在Foo中声明了一个嵌套类,它被称为ImplementationDetail”。然后你继续说“等待等待,它不是一个新的类,它完全是另一个别的东西”,可以理解编译器会感到困惑。

您是否曾尝试过声明库的实现并使用它而不是尝试创建别名?

答案 3 :(得分:0)

在您的第一个代码中,您将嵌套类型ImplementationDetail声明为struct,它将在Foo内定义。尝试别名它不起作用,因为这将是其他地方定义的类型,实际上,您无法从类外部访问private结构。将指针包装到内部的另一个对象似乎是不必要的:您可以改为按值嵌入Library::SomeStruct或从ImplementationDetail导出Library::SomeStruct

struct ImplementationDetail
    : Library::SomeStruct {
    using Library::SomeStruct::SomeStruct;
};

using声明仅用于从Library::SomeStruct继承所有构造函数。)

答案 4 :(得分:0)

我认为如果没有施法,这是不可能的。

基本上有两种方法可以做到:

1)将p_定义为void*并将其投射到使用它的每个函数中。

/* HEADER */
class Foo {
    private:
        void* p;
};

/* SOURCE */
#include <Library.h>

void Foo::AnyFunc()
{
    Library::SomeStruct* pImpl = reinterpret_cast<Library::SomeStruct*>(p);
    ...
}

2)创建一个类的“阴影”类(在.cpp文件中),克隆所有成员并将p_定义为Library :: SomeStruct。然后将this指针强制转换为此阴影类。这当然是一个非常不安全和肮脏的黑客,我不建议......

/* HEADER */
class Foo {
    private:
        void* p;
};

/* SOURCE */
#include <Library.h>

class FooImpl
{
public:
    void AnyFunc() { p->DoSomething(); }

private:
    Library::SomeStruct* p;
}

void Foo::AnyFunc()
{
    FooImpl* pImpl = reinterpret_cast<FooImpl*>(this);
    pImpl->AnyFunc();
}

这会利用内存结构,因此非常脆弱(所有成员需要按相同的顺序排列,当您添加或删除成员时,您也需要更新ShadowFoo)。我提到这只是为了完整。

3)这将我们带到另一个但更简单的方法:在源文件中创建实现并使用void*在指针构建器中初始化它: -

/* HEADER */
class Foo {
    private:
        void* p;
};

/* SOURCE */
#include <Library.h>

class FooImpl
{
public:
    FooImpl(void* pSomeStruct)
    {
        p = reinterpret_cast<Library::SomeStruct*>(pSomeStruct);
    }

    void AnyFunc() { p->DoSomething(); }

private:
    Library::SomeStruct* p;
}

void Foo::AnyFunc()
{
    FooImpl impl = FooImpl(p);
    impl.AnyFunc();
}