PIMPL идиома для указателя на класс в C ++ - PullRequest
0 голосов
/ 13 ноября 2018

У меня есть рабочий интерфейс для двух программ (ProgramA и ProgramB), которые я бы хотел улучшить, насколько это возможно, для разделения обеих программ.Случай, который я хочу охватить, - это вызов из ProgramA класса из ProgramB (Compute_Prop), который может быть инициализирован только с некоторыми аргументами, которые я сейчас не делаю заранее.Следовательно, я использую указатель в заголовке.В настоящее время у меня есть что-то вроде этого:

interface.h

#include "programB.h" // loads Compute_Prop

class Compute {
  public:
    Compute();
    Compute(targ1 arg1, targ2 arg2);
    ~Compute();
    // some methods ...
  private:
    Compute_Prop* compute;
};

interface.cpp

#include "programB.h"
#include "interface.h"

#include "programA.h"

Compute::Compute() = default;

Compute::~Compute() {                                                                                                     
    delete compute;                                                                                                                                                                                                                                                                                                                                                                   
}

Compute::Compute(arg1, arg2) {

  // do something ... to get data

  compute = new Compute_Prop( &data, arg2 );
}

Затем я пытаюсь имитировать идиому PIMPL со следующим

interface.h

#include "programB.h" // loads Compute_Prop

class Compute {
  public:
    Compute();
    Compute(targ1 arg1, targ2 arg2);
    ~Compute();
    // some methods ...
  private:
    class PIMPL;
    PIMPL* compute;
};

interface.cpp

#include "programB.h"
#include "interface.h"

#include "programA.h"

Compute::PIMPL = Compute_Prop;

Compute::Compute() = default;

Compute::~Compute() {                                                                             
    delete compute;                                                                                                                                                                     
}

Compute::Compute(arg1, arg2) {

  // do something ... to get data

  compute = new Compute_Prop( &data, arg2 );
}

но компилятор говорит:

error: expected unqualified-id
  Compute::PIMPL = Compute_Prop;
                 ^

Я предполагаю, что это как-то связано с тем, что Compute_Prop не имеет пустого конструктора.Я не могу придумать что-то, что работает.Что я должен делать?Может быть, что-то вроде указателя на указатель?В качестве ограничения я не могу изменить programB.

Примечание. Как, вероятно, уже ясно из приведенного выше, мое понимание низкого уровня C ++ / C является недостаточным.

РЕДАКТИРОВАТЬ: я представил исправления, предложенные @nm и @Matthieu Brucher

Ответы [ 2 ]

0 голосов
/ 18 ноября 2018

Возможно, я наткнулся на простое решение. Я публикую это здесь, чтобы вы могли судить, адекватно ли это, или даже если оно может быть улучшено - конечно. Я убежден, что полиморфизм во время выполнения не нужен, даже полиморфизм. В любом случае переменная-член compute будет указателем на тип Compute_Prop. Затем, учитывая, что производительность здесь имеет решающее значение: зачем запускать дополнительные издержки на виртуальные функции-члены?

Суть в том, чтобы достичь реализации, которая скрывает включение Compute_Prop без потери производительности. Как? Это конкретное решение использует шаблонный класс и затем явную реализацию. Дело в том, что создание экземпляров может быть сделано в реализации. Получил от Fluent C ++ в блоге . Кроме того, в этом посте есть подсказки о том, как должна выполняться реализация. Прототип будет:

interface.h

template <typename T>
class Compute {
  public:
    Compute();
    Compute(targ1 arg1, targ2 arg2);
    ~Compute();
    // some methods ...
  private:
    T* compute; // No need to state that is going to be T:=Compute_Prop
};

interface_impl.h

#include "interface.h"    
#include "programA.h"

template <typename T>
Compute::Compute() = default;

template <typename T>
Compute::~Compute() {                                                                                                              
    delete compute;                                                                                                                                                                                                                                                                                                                                                                
}

template <typename T>
Compute::Compute(arg1, arg2) {

  // do something ... to get data

  compute = new T( &data, arg2 );
}

interface.cpp

 #include "interface.h"
 #include "interface_impl.h"
 #include "programA.h"
 #include "programB.h" // loads Compute_Prop

 int main(int argc, char** argv) {

   template class Compute<Compute_Prop>;

 }

Еще один связанный вопрос , который может быть полезен для людей с той же дилеммой.

0 голосов
/ 13 ноября 2018

Ваша реализация должна использовать интерфейс (или фактически класс с только абстрактными методами) в качестве базового класса.Вы не можете назначать типы в C ++.Вы можете создавать только typedef и псевдонимы, например:

using PIMPLType = Compute_Prop;

Однако в вашем случае это не сработает.Вот как это должно быть реализовано (также с возможностью нескольких реализаций):

class IImplementation
{
public:
    virtual void saySomething() = 0;
};

class ImplementationA : public IImplementation
{
public:
    virtual void saySomething() override {
        std::cout << "A";
    }
};
class ImplementationB : public IImplementation
{
public:
    virtual void saySomething() override {
        std::cout << "B";
    }
};

class Foo {
    IImplementation *pimpl;
public:
    Foo()
        : pimpl(new ImplementationA)
    {}

    ~Foo() { delete pimpl; }

    void saySomething() {
         pimpl->saySomething();
    }
};
...