Как опустить частные не виртуальные методы из определения класса? - PullRequest
2 голосов
/ 04 апреля 2011

Допустим, у меня есть что-то вроде следующего:

a.hpp:

class B;

class A
{
private:
  std::unique_ptr<B> b_;
}

a.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

something_complicated& A::get_complicated() { return b_->x; }

К сожалению, в этом случае a.cpp упадет для компиляции, потому что "get_complicated ()" не является методом A.

Итак, мы можем попробовать это:

a.hpp:

class B;

class A
{
private:
  std::unique_ptr<B> b_;
  something_complicated& A::get_complicated();
}

Но тогда a.hpp не скомпилируется, потому что что-то сложное не определено.

Мы могли бы заранее объявить что-то сложное, если это класс, но, вероятно, это typedef, так что его нет.

Единственный способ, которым я могу думать об этом, не делая b_ публичным и не включая что-то в_компилированный.hpp в a.hpp, заключается в следующем:

a.cpp:

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

#define get_complicated ((b_->x))

Конечно, мне не нужно определять макрос, чтобы обойти эту проблему? Есть альтернативы?

Ответы [ 6 ]

2 голосов
/ 04 апреля 2011

Возможно, самое простое решение - заключить ссылку на сложный тип в классе, объявить его в a.hpp и определить его в something_complicated.hpp.

a.hpp:

class B;
class complicated_ref;

class A
{
public:
  complicated_ref get_complicated();
private:
  std::unique_ptr<B> b_;
};

something_complicated.hpp:

// ... complicated definitions ...
typedef whatever something_complicated;

struct complicated_ref
{
    complicated_ref(something_complicated & thing) : ref(thing) {}
    something_complicated & ref;
};

Теперь a.cpp, и все, что требует использования сложного типа, должно включать его заголовок, но все, что просто хочет использовать class A, не нуждается в.

Предполагается, что у некоторых клиентов A есть веская причина для доступа к сложному объекту, но B должен быть недоступен для всех.Было бы еще проще разрешить доступ к B, когда это необходимо, и через него добраться до сложной вещи.

1 голос
/ 04 апреля 2011

Боюсь, что существует неправильное понимание того, что принадлежит классу, а что нет.

Не все методы, которые действуют на внутренние компоненты класса, должны быть методами класса, в конце концов, у нас уже есть friend функции. Я знаю, что многие люди объявляют методы helper как частные функции, однако при этом возникают ненужные зависимости (время компиляции) и проблема видимости с friend s.

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

  • Создание Impl (B в вашем случае) истинным классом с собственной логикой проверки и истинным API
  • Использование static свободных функций (или функций, объявленных в анонимном пространстве имен)

Оба хороши и используют то, что кажется наиболее подходящим. А именно:

  • методы: при решении вопросов проверки
  • свободные функции: для вычислений, которые могут быть выражены с помощью вышеупомянутых методов

С моей стороны намеренно искать как можно меньше методов, потому что это единственные, которые могут испортить мои классовые инварианты, и чем меньше они, тем увереннее я буду в том, что инварианты будут поддерживаться .

В вашем случае вам решать, какой подход вам больше подходит.

В действии:

a.cpp

#include <something_complicated.hpp>

struct B
{
  something_complicated x;
}

static something_complicated& get_complicated(B& b) { return b_.x; }

// or anonymous namespace instead
namespace {
  something_complicated& get_complicated(B& b) { return b_.x; }
}

Не так сильно отличается от того, что у тебя было, а?

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

1 голос
/ 04 апреля 2011

Что не так с:

a.hpp:

class B;

class A
{
private:
  std::unique_ptr<B> b_;
public:
  B& get_B();
}

Если ваши клиенты хотят получить что-то сложное из B, то пусть они #include <something_complicated.hpp>.

1 голос
/ 04 апреля 2011

Просто избегайте ссылки на something_complicated в a.hpp.

Одним из решений является замена функции-члена get_complicated на свободную функцию или статический метод другого класса.

.h :

class A_impl_base {
    A_impl_base() {}

    friend class A_impl; // all instances of A_impl_base are A_impl
}; // this stub class is the only wart the user sees

class A
{
private:
    std::unique_ptr< A_impl_base > b_; // this is not a wart, it's a pimpl

    friend class A_impl;
}

.cpp :

class A_impl : A_impl_base {
     static A_impl &get( A &obj ) { return * obj.b_; }
     static A_impl const &get( A const &obj ) { return * obj.b_; }
};
0 голосов
/ 04 апреля 2011

Если вы контролируете something_complicated.hpp, вы можете делать то, что делает стандартная библиотека: создать something_complicated_fwd.hpp, который имеет соответствующие предварительные объявления, включая типы, которые могут быть или не быть typedefs.

0 голосов
/ 04 апреля 2011

Мы могли бы заранее объявить что-то сложное, если это класс, но это, вероятно, typedef, так что его нет.

Это именно то, что вам нужно сделать.И я не понимаю, как определение типа typedef исключает предварительное объявление.

...