Шаблон проектирования C ++: несколько способов загрузки файла - PullRequest
3 голосов
/ 23 марта 2012

Описание: В поисках стандартного шаблона проектирования C ++ для загрузки различных файлов через конструктор

У меня есть класс Base с некоторыми функциями, которые будут использоваться всеми производными классами (например, Derived_A, Derived_B).Принципиальное отличие состоит в том, что Derived_A и Derived_B переопределяют функцию load, которая используется конструктором для загрузки файла данных (load также может быть вызван явно вне конструктора).

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

Это пахнет как классическая проблема, но я не могу придумать, как это сделать (и я совсем недавно программировал на Python, который, я считаю, из-за слабой типизации всегда вызывал бы намеченную функцию).

В том же духе я бы очень хотел, чтобы Base::load был чисто виртуальным / абстрактным (будут создаваться только производные классы);однако, это не скомпилируется (я полагаю, потому что компилятор видит, что будет вызываться чисто виртуальная функция).

Вы можете помочь?

Вывод:

Загрузка с конструктором:
База :: загрузить файл_A
База :: загрузить файл_B Загрузка с функцией пост-строительства:
Derived_A :: загрузить файл_A
Derived_B ::загрузить файл_B

код:

#include <iostream>
#include <string>

class Base
{
public:
  Base() {}
  Base(std::string x)
  {
    load(x);
  }
  virtual void load(std::string x)
  {
    std::cout << "\tBase::load " << x << std::endl;
  }
};

class Derived_A : public Base
{
public:
  Derived_A() {}
  Derived_A(std::string x): Base(x) {}
  void virtual load(std::string x)
  {
    std::cout << "\tDerived_A::load " << x << std::endl;
  }
};

class Derived_B : public Base
{
public:
  Derived_B() {}
  Derived_B(std::string x): Base(x) {}
  void virtual load(std::string x)
  {
    std::cout << "\tDerived_B::load " << x << std::endl;
  }
};

int main()
{
  // simpler code, but it doesn't behave as I hoped
  std::cout << "Loading w/ constructor:" << std::endl;
  Base*der_a = new Derived_A(std::string("file_A"));
  Base*der_b = new Derived_B(std::string("file_B"));

  // this is what I want to do
  std::cout << "Loading w/ function post construction:" << std::endl;
  der_a = new Derived_A;
  der_a->load( std::string("file_A") );
  der_b = new Derived_B;
  der_b->load( std::string("file_B") );
  return 0;
}

Ответы [ 2 ]

2 голосов
/ 23 марта 2012
2 голосов
/ 23 марта 2012

Поведение, которое вы видите, хорошо определено в C ++ - оно просто бесполезно в этом сценарии, поскольку класс не полностью создан, когда вы вызываете load(std::string) из Base::Base(std::string).

Существует два непосредственных подхода:

A

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

class Loader 
{
public:
    Loader(Base* const p, const std::string& location) : d_base(p) 
    {
        this->d_base->load(location);
    }

private:
    std::unique_ptr<Base>d_base;
private:
    Loader(const Loader&) = delete;
    Loader& operator=(const Loader&) = delete;  
};

Используется:

std::cout << "Loading w/ Loader:\n";
Loader l_der_a(new Derived_A, "file_A");
Loader l_der_b(new Derived_B, "file_B");

B

Вы также можете подойти к нему с помощью вспомогательной функции:

class Base {
public:
    template<typename T>
    static void Load(const std::string& x) 
    {
         T().load(x);
    }

    Base() 
    {
    }

    Base(std::string x) 
    {
         /* load(x); << see Load(const std::string&) */
    }

    virtual ~Base() 
    {
    }

    virtual void load(std::string x) = 0;
};

Используется:

std::cout << "Loading w/ Base::Load<T>():\n";
Derived_A::Load<Derived_A>("file_A");
Derived_B::Load<Derived_B>("file_B");

А потом есть несколько других подходов и вариантов - это зависит от того, что подходит вашему дизайну лучше всего. С C ++ у вас наверняка есть варианты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...