Ошибка неполного типа при использовании вложенного класса в наборе

Я работаю над переводом кода Java на C++. Когда я пытаюсь написать код вроде:

.h:

class A {
  private:
    class B;
    std::set<B> b_set;
};

.cpp:

class A::B {
};

Я получил неполную ошибку типа. Я понимаю, что это потому, что вложенный класс неполный, прежде чем использовать его в b_set. Но как лучше всего это исправить?


person xieziban    schedule 31.03.2014    source источник


Ответы (2)


Вы можете описать весь свой B класс в файле .h.

Вот рабочий пример.

#include<set>
class A {
  private:
    class B{
        B():foo(1){}
        int foo;
    };
    std::set<B> b_set;
};

Однако, если вы хотите разделить свое определение и экземпляр, вы можете сделать это:

А.ч

#include<set>
class A {

  private:
    class B{
      public:
      B();
      private:
      int someMethod();
      int foo;
    };
    std::set<B> b_set;
};

A.cpp

#include "A.h"
  A::B::B():foo(1){}
  int A::B::someMethod(){
    return 42;
  }

Вообще говоря, вложенные классы могут быть серьезным PITA из-за всех обручей, через которые вам нужно перепрыгнуть, чтобы получить доступ к чему-либо из них.

Еще один хороший справочник по вложенным классам: определение вложенного класса в исходном файле

person AndyG    schedule 31.03.2014
comment
Спасибо, Энди! Но в данном случае реализация находится в файле .h. Это хорошая практика? - person xieziban; 10.04.2014
comment
@xieziban: Смотрите мою правку. Вам понадобится действительно хороший аргумент для использования вложенного класса, поэтому общее заявление о том, что его вообще нет. - person AndyG; 11.04.2014

Ну, я опоздал, я знаю, все же я хочу указать на другую возможность, если вы хотите полностью скрыть внутренности класса B:

class A
{
  private:
  class B;
  std::set<B*> b_set;
};

Обратите внимание на использование указателей в наборе. Однако остается важное отличие: поскольку вставляются только указатели, вы все равно можете вставлять указатели на разные экземпляры с одинаковым содержимым. Чтобы решить эту проблему, вам нужен собственный компаратор:

class A
{
  private:
  class B;
  struct Less
  {
    bool operator() (B const* x, B const* y) const
    {
      return *x < *y;
    }
  };
  std::set<B*, Less> b_set;
};

Имейте в виду (это не упоминалось в предыдущем ответе, но там тоже требуется!), что должен быть определен компаратор для B (B или ссылка на, не указатель!):

A.h

#include <set>
class A
{
  private:
  class B;
  struct Less
  {
    bool operator() (B const* x, B const* y) const;
  };
  std::set<B*, Less> b_set;
};

A.cpp

class A::B
{
  friend bool Less::operator() (B const* x, B const* y) const;
  bool operator<(B const& other) const
  {
    return foo < other.foo;
  }
  int foo;
};

bool A::Less::operator() (B const* x, B const* y) const
{
  return *x < *y;
}

Это позволяет полностью скрыть B из заголовка, если вы хотите или должны по какой-либо причине. Однако вы больше не можете вставлять объекты из стека напрямую, так как они не копируются, и у вас есть указатели на стек, которые быстро становятся недействительными. Особое внимание следует уделить удалению объектов, когда они больше не нужны, иначе возникнут утечки памяти. Помните, что в Java нет сборки мусора. Если вы используете С++ 11, вы можете решить проблему, используя ::std::unique_ptr, до, ::std::auto_ptr:

A.h

#include <set>
#include <memory>
class A
{
  private:
  class B;
  struct Less
  {
    bool operator() (B const* x, B const* y) const;
  };
  std::set<std::unique_ptr<B>, Less> b_set;
};
person Aconcagua    schedule 09.10.2015
comment
Ваш ответ мне очень помогает.thnx - person Buddhika Chaturanga; 18.07.2016