Почему метод должен быть объявлен в определении класса C++?

Если только определить этот метод, он будет иметь ошибку компилятора.

void classA::testMethod() {    
}

и поэтому он должен быть объявлен первый:

class classA {    
   void testMethod();
};

почему они должны быть объявлены?

Я знаю, что для общих методов метода C не нужно объявлять, но их можно просто определить:

void test() {
}

9 ответов


существует несколько вторичных причин, как объективных, так и субъективных (т. е. позволяет указать видимость, служит интерфейсом для класса и, вероятно, несколько других, связанных с компиляцией и связыванием фаз и видимости символов TU, не говоря уже о том, что класс является базовой единицей инкапсуляции со всем, что подразумевает), но бесспорным является то, что стандарт диктует это:

N3797 - класса.mfct / p2

функция-член может быть определенный (8.4) в своем определении класса, в в каком случае это встроенная функция-член (7.1.2), или это может быть определено вне определения класса, если оно уже было объявлено, но не определено в определении класса. функции-члена определение, которое появляется вне определения класса, должно отображаться в области пространства имен включающего определение класса. За исключением члена определения функций, которые появляются вне определения класса, и за исключением явного специализации функций-членов класса шаблоны и шаблоны функций-членов (14.7), появляющиеся за пределами определение класса, функция-член не должны быть объявлены повторно.

выделено мной.


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

это может показаться противоречием, но определение также является объявлением. Итак, это означает, что определение может появиться в самом классе:

class A {
  void testMethod() { /*...*/ } 
};

[редактирование] Кроме того, практически говоря, внутри класса есть private, protected и public запасные части. Это необходимо для инкапсуляция. Если бы вы могли объявить методы вне класса, вы бы потеряли инкапсуляцию. Любой может получить доступ к частным членам, просто определив дополнительных геттеров и сеттеров, даже если это не имеет смысла. Инварианты классов станут бессмысленными.


Это помогает инкапсуляции. Если у вас есть класс

class A {
public:
  foo();
  bar();
}

вы можете быть уверены, что только методы foo и bar беспорядок с частными членами данных класса. (Или магия указателя или неопределенное поведение, конечно)


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

Это правда для большинства (но не всех) языках, которые поддерживают понятие класса.

Это также объясняет, почему ситуация отличается для пространства имен (которые не закрыты).


обратите внимание на использование квалификатора ::. Это значит

  • слева у вас есть пространство имен или идентификаторов класс
  • справа у вас есть пространство имен, класс или идентификатор метода / функции

так писать!--4--> предполагает, что существует класс или пространство имен A defined-так определяется C++. И это относится к

void A::testMethod();

а так же

void A::testMethod()
{
}

Также обратите внимание на глобальное пространство имен, где вы действительно, слева от :: а в

void ::testMethod()
{
}

и по определению глобальное пространство имен всегда определяется так, что приведенный выше код определяет функцию, подобную C-style без квалификатора.


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

  1. вы можете только что-то объявить как public, private или protected в объявлении класса, вы не можете сделать это в определении метода .файл cpp. Итак, какая видимость будет иметь ваш метод автономного класса?

  2. если стандарт решил выбрать один из трех в качестве default (C++ по умолчанию private) и поместите эту видимость в свой метод, теперь у вас есть проблема с областью видимости. Даже самая ограничительная видимость (private) означает, что вы можете использовать метод в любом другом методе-члене,включая те, которые определены перед ним в исходном файле. Без объявления в определении класса эти более ранние функции не будут знать о вашем свободном методе, поэтому вы нарушаете правила области видимости.

в вашем foo.ч заголовок:

class foo
{
public:
    foo() {}
    virtual ~foo() {}

    declaredMethod();
};

в вашем foo.cpp

foo::declaredMethod()
{
    ...
    freeStandingMethod();   // This should be legal, but it can't work since we
                            //  haven't seen foo::freeStandingMethod() yet
    ...
}

foo::freeStandingMethod()
{
    ...
}

даже если вы могли бы сделать эту работу в той же .cpp файл, это законно разместить foo::freeStandingMethod() в другой .файл cpp с foo::declaredMethod() в этот момент это становится невозможным.


Это не ответ, но вы можете найти его информативным и интересным. Добавление 2 функций шаблона в ваш класс позволит вам вызвать любую свободную функцию, которая принимает объект этого класса в качестве первого параметра:

#include <string>
#include <iostream>

struct puppy {
   puppy(std::string name)
   : _name(std::move(name))
   {}

   const std::string& name() const noexcept {
      return _name;
   }

  void set_name(std::string name) {
    _name = std::move(name);
  }

    template<class F, class ...Args>
      auto perform(F&& f, Args&&...args) const 
      -> decltype(f(*this, std::forward<Args>(args)...))
    {
      return f(*this, std::forward<Args>(args)...);
    }

    template<class F, class ...Args>
      auto perform(F&& f, Args&&...args) 
      -> decltype(f(*this, std::forward<Args>(args)...))
    {
      return f(*this, std::forward<Args>(args)...);
    }

private:
   std::string _name;
};


void woof(const puppy& p) {
   std::cout << "puppy " << p.name() << " woofs!" << std::endl;   
}

void indented_woof(const puppy&p, size_t indent) {
    std::cout << std::string(indent, ' ');
    woof(p);
}

void complex_woof(const puppy& p, int woofs)
{
    std::cout << "attention!" << std::endl;
  for (int i = 0  ; i < woofs ; ++i) {
    p.perform(indented_woof, 4);
  }
}

std::string name_change(puppy& p, std::string(new_name))
{
  auto old_name = p.name();
  p.set_name(std::move(new_name));
  return old_name;
}

int main()
{
  puppy fido { "fido" };

  fido.perform(woof);
  fido.perform(complex_woof, 10);
  auto old_name = fido.perform(name_change, "bonzo");
  fido.perform(woof);
  std::cout << "changed name from " << old_name << std::endl;
  return 0;
}

"методы" или "функции-члены" (как более распространенная терминология в C++) являются частью объявления класса. Поскольку необходимо объявить класс C++ в одном месте, необходимо убедиться, что все "функции-члены" (или "методы") уже присутствуют в этом объявлении.

Я знаю, что для общих методов метода C не нужно объявлять, но они можно просто определить

когда вы ссылаетесь на "общий метод C" в C++, вы на самом деле имеете в виду " общий функция." Обратите внимание, что вы можете объявлять классы, где вы можете объявлять такие функции.

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

class A{
   void privateMethod() {
       // do something here...
   }
public:
   void publicMethod() {
       // do something here...
   }
};

why should declare these? I know for common c method, there is no need to declare, instead of it just define it:

в C нет метода, просто атрибут структуры, который может быть функцией Pointeur, а затем связан с функцией addresse.

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

компилятор преобразует это предварительное объявление в функцию pointeur, а затем связывает с указанным methode int построение вашего объекта.

Если определение класса C++ должно быть преобразованный в структуру C, код будет выглядеть так:

struct Aclass {
 void (*Amethode))(int);
}
void Amethode(int) { return (0); }
Aclass primaryObject = {&Amethode};
Aclass* AclassConstructor() {
 Aclass* object;
 object = malloc(sizeof(Aclass));
 memcpy(object, primaryObject, sizeof(Aclass));
 return (object);
}