Объявления класса C ++ и пространства имен - PullRequest
0 голосов
/ 24 января 2011

У меня проблема с библиотекой C ++, которую я пытаюсь написать. Это обычная настройка, один файл cpp, один файл заголовка. Я хочу, чтобы файл заголовка отображал только те части, которые предназначены для использования (например, у меня есть абстрактный базовый класс, я не хочу в файле заголовка). Пока что я просто работаю с одним файлом (я полагаю, что это не должно иметь никакого значения, поскольку включения выполняются препроцессором, который ни о чем не заботится).

Вы заметите, что «файл заголовка» расположен в двух местах, до и после файла реализации заголовка.

#include <stdio.h>

// lib.h
namespace foo {
    template <class T> class A;
}

// lib.cpp
namespace foo {
    template <class T> class A {
        private:
        T i;
        public:
        A(T i) {
            this->i = i;
        }

        T returnT() {
            return i;
        }
    };
};

// lib.h
namespace foo {
    template <class T> T A<T>::returnT();
}

// foo.cpp
void main() {
    foo::A<int> a = foo::A<int>(42);
    printf("a = %d",a.returnT());
}

Итак, естественно, я бы хотел, чтобы мой заголовочный файл содержал только

namespace foo {
    template <class T> class A;
    template <class T> T A<T>::returnT();
}

Но моему компилятору это не нравится (он жалуется, что returnT не является членом foo::A<T>. Причина, по которой я не хочу помещать объявление класса в заголовок, заключается в том, что тогда это произойдет (как я поймите это), содержат все личные и подобные вещи, которые я хотел бы скрыть.

Может быть, это только я, но следующий заголовочный файл кажется "плохим", по крайней мере, как "спецификация интерфейса". Он раскрывает некоторые из внутренних элементов A, о которых пользователю библиотеки не нужно знать.

// lib.h
namespace foo {
    template <class T> class A {
        private:
        int i;
        public:
        A(T);
        T returnT();
    };
}

// lib.cpp
namespace foo {
    template <class T> A<T>::A(T i) {
        this->i = i;
    }
    template <class T> T A<T>::returnT() {
        return i;
    }
};

Это приемлемый способ сделать это? Я бы хотел более абстрактный заголовочный файл, если это вообще возможно.

Ответы [ 2 ]

5 голосов
/ 24 января 2011

Вы не можете отделить определение шаблона от его объявления. Они оба должны вместе войти в заголовочный файл.

Для "почему?" Я рекомендую прочитать «Почему я не могу отделить определение своего класса шаблонов от его объявления и поместить его в файл .cpp?» .


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

namespace foo {
    template <class T> class A;     
    template <class T> T A<T>::returnT(); 
} 

Недопустимо по той же причине, что это недопустимо:

namespace foo {
    class A;
    int A::returnT();
} 

Функции-члены должны быть объявлены внутри определения класса.

2 голосов
/ 24 января 2011

Есть две проблемы с файлами .cpp, с которыми вы здесь работаете:

I.Если вы хотите поместить экземпляр этого класса в стек (как вы делаете это в main ()), компилятору необходимо знать размер класса (чтобы выделить достаточно памяти).Для этого ему нужно знать членов и тем самым полное объявление.

Единственный способ скрыть компоновку класса - создать интерфейс и фабричный метод / функцию и поместить экземпляр в кучу.на заводе.

Как пример (без шаблона; см. ниже, чтобы узнать почему):

namespace foo {
  class IA {
    public:
      virtual ~IA();
      virtual int returnT() = 0;

      static IA *Create();
  };
}

В вашем .cpp вы затем делаете:

namespace foo {
  class A : public IA {
    private:
      int i;
    public:
      A() : 
        i(0) {
      }
      virtual ~A() {
      }
      virtual int returnT() {
        return i;
      }
  };
  IA::~IA() {
  }

  IA *IA::Create() {
    return new A();
  }
}

Кстати: использование интеллектуальных указателей будет предложено ...

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

Таким образом, вы можете разделить свой код на lib.h и lib_impl.h:

lib.h:

namespace foo {
  template <typename T> class IA {
    public:
      virtual ~IA() {
      }
      virtual T returnT() = 0;

      static IA *Create();
  };
}

lib_impl.h:

namespace foo {
  template <typename T> class A : public IA<T> {
    private:
      T i;
    public:
      A() : 
        i(T()) {
      }
      virtual ~A() {
      }
      virtual T returnT() {
        return i;
      }
  };

  template <typename T> IA<T> *IA<T>::Create() {
    return new A<T>();
  }
}

, поэтому вы включаете lib_impl.h везде, где вам нужны реализации.Чтобы использовать явные экземпляры, добавьте lib.cpp и просто разрешите этому файлу включать lib_impl.h:

lib.cpp:

#include <lib_impl.h>
namespace foo {
  template class IA<int>;
  template class A<int>;
  template class IA<float>;
  template class A<float>;
  template class IA<char>;
  template class A<char>;
  // ...
}
...