2013-12-16 3 views
3

В моем классе мне нужно сохранить указатель на структуру, которая определена в библиотеке, которую я использую для ее реализации. Поскольку эта библиотека используется только в файле реализации, я бы хотел не включать ее в заголовок напрямую. В то же время я хочу избежать загрязнения пространства имен. Таким образом, я хотел бы сделать:Типичный прототип частной структуры в исходном файле

/* 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_; 
} 

Есть ли способ, чтобы избежать двойного разыменования? Является причиной моего нерабочего первого решения из-за неизвестных размеров указателя?

+0

_ «Является причиной моего нерабочего первого решения из-за неизвестных размеров указателя?» «Неработающий» довольно расплывчатый, каковы ваши конкретные проблемы компиляции/времени выполнения? –

+0

Это зависит от того, где я поставлю фактический прототип. Если он является частным, он жалуется на это. Если он является общедоступным, он жалуется, что Foo не является пространством имен. – Svalorzen

+0

Вы можете найти шаблон частной реализации (PIMPL). По-моему, я читал об этом в большой книге Design Patterns. –

ответ

0

Это неправильное заявление:

using Foo::ImplementationDetail = Library::SomeStruct; 

using не работает таким образом. В C++ 11 using не может создать псевдоним для имени в одном пространстве имен для имени в другом пространстве имен. В C++ 03 все using делает перенос некоторого другого пространства имен в глобальную видимость в текущем блоке перевода. Он не используется для создания псевдонимов в C++ 03, как вы, кажется, хотите сделать здесь.

Pimpl является де-факто способ делать то, что вы пытаетесь сделать, но в файле заголовка вместо того, чтобы использовать ImplementationDetail*, я хотел бы использовать простой void*. Использование того, таким образом void* гарантированно будет правильно в соответствии со стандартом:

class Foo { 
    private: 
     void * pImpl; 

Используйте static_cast перейти от void* к вашему фактическому типу:

void Foo::Bar() 
{ 
    Library::SomeStruct* thingy = static_cast <Library::SomeStruct*> (pImpl); 
    // ... 
} 

Вы можете избежать с помощью void* в соответствии с заявкой на ваш адрес:

namespace Library 
{ 
    struct SomeStruct; 
}; 

class Foo 
{ 
private: 
    Library::SomeStruct* pStruct; 
}; 

И тогда в реализации не нужно уродливого броска.


Использование static_cast: Или reinterpret_cast

+0

Не можете ли вы 'static_cast' ничего от пустоты? –

+0

@MarkB: Да, конечно. Я полагаю, что это было бы предпочтительнее - я использую 'reinterpret_cast' из-за привычки, отчасти из-за моей концепции, что это немного более понятно для других программистов. Последнее может быть ложным. –

+1

В C++ 11 'использование' также используется для объявления псевдонимов типов. Единственная проблема заключается в том, что вы не можете поместить объявление в другую область. –

0

Причины вы не можете принять ваш первый подход является то, что в заголовке компилятора «я объявляю вложенный класс в Foo и это называется ImplementationDetail ". Затем вы переходите к утверждению: «Подождите, подождите, это НЕ новый класс, это псевдоним для этой цели», и, понятно, компилятор запутался.

Вы пробовали просто вперед, объявив реализацию библиотеки и используя это вместо того, чтобы пытаться создать псевдоним?

+0

Я об этом подумал. Проблема в том, что библиотека, которую я использую, находится в C, и, таким образом, я бы создал глобальное имя для всех, кого бы никто не заботился. – Svalorzen

0

В вашем первом коде вы указали вложенный тип ImplementationDetail как struct, который будет определяться внутри Foo. Пытаться к псевдониму он не может работать, потому что это будет тип, определенный в другом месте, и, фактически, структура private недоступна извне класса.Обертывание указателя на другой объект внутри кажется ненужным: вы могли бы вместо того, чтобы либо вставлять Library::SomeStruct по значению или иметь ваш ImplementationDetail проистекает из Library::SomeStruct:

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

(using декларации используется только наследовать все конструктор из Library::SomeStruct).

0

Я думаю, что это невозможно без литья.

В основном есть два способа сделать это:

1) Определение р-, как void* и брось в каждой функции, которая использует его.

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

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

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

2) Создание «теневое» -класса вашего класса (в .cpp-файл) со всеми членами клонированы и р- определяется как библиотека :: 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(); 
} 
+0

Я бы поднял ваш ответ (над другими, которые уже правильные), как только вы показываете образцы рабочего кода. –

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

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

и распределение/deallocating/dereferencing указатель в этом исходном файле должен работать только отлично.

+0

+1 хорошая идея ... –

+0

В любом случае это будет работать, если, скажем, в библиотеке содержится метод SomeStruct * getNewStruct()? – Svalorzen

Смежные вопросы